john's tech blog

hope is coming


  • 首页

  • 标签

  • 归档

angular2源码之forwardRef

发表于 2017-08-04 | 更新于 2019-05-07

今天抽空看了下angular2的依赖注入部分的源码,看到forward_ref.ts这个文件,源码位置如下

forward_ref.ts

forwardRef

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import {Type} from '../type';
import {stringify} from '../util';

//混合类型
//相当于可以是任意方法来实现这个接口
export interface ForwardRefFn { (): any; }

//forwardRefFn加入属性__forward_ref__
export function forwardRef(forwardRefFn: ForwardRefFn): Type<any> {
(<any>forwardRefFn).__forward_ref__ = forwardRef;
(<any>forwardRefFn).toString = function() { return stringify(this()); };
return (<Type<any>><any>forwardRefFn);
//TypeScript也使用尖括号来表示类型断言,JSX的语法带来了解析的困难。因此,TypeScript在 .tsx文件里禁用了使用尖括号的类型断言。
//类型断言好比其它语言里的类型转换,但是不进行特殊的数据检查和解构。 它没有运行时的影响,只是在编译阶段起作用。 TypeScript会假设你,程序员,已经进行了必须的检查。
}

//如果type是function 而且有__forward_ref__属性 且 __forward_ref__为forwardRef,那么就执行这个type
export function resolveForwardRef(type: any): any {
if (typeof type === 'function' && type.hasOwnProperty('__forward_ref__') &&
type.__forward_ref__ === forwardRef) {
return (<ForwardRefFn>type)();
} else {
return type;
}
}

编译为js后是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
export function forwardRef(forwardRefFn) {
forwardRefFn.__forward_ref__ = forwardRef;
forwardRefFn.toString = function () { return stringify(this()); };
return forwardRefFn;
}

export function resolveForwardRef(type) {
if (isFunction(type) && type.hasOwnProperty('__forward_ref__') &&
type.__forward_ref__ === forwardRef) {
return type();
}
else {
return type;
}
}

Type

1
2
3
4
5
6
7
8
9
//const是对let的一个增强,它能阻止对一个变量再次赋值。
export const Type = Function;

export function isType(v: any): v is Type<any> {
return typeof v === 'function';
}

//类可以创建出类型,所以你能够在允许使用接口的地方使用类。
export interface Type<T> extends Function { new (...args: any[]): T; }

注意到在导出interface Type时,我们定义了constructor,constructor存在于类的静态部分

涉及到的知识点:

1.typescript的interface

2.typescript的export

3.typescript的const

4.typescript的继承

5.typescript的类型别名

angular源码分析之scope步步解析

发表于 2017-08-02 | 更新于 2019-05-07

看了Scopes and DirtyChecking这篇文章深有体会,所以打算简单翻译一下以做记录。

Scope对象

Scopes能通过Scope的构造函数new来创建。结果就是一个简单的javascript对象。先创建一个简单的例子。

1
2
3
4
function Scope() {
}
var scope = new Scope();
scope.aProperty = 1;

Watching对象属性:$watch和$digest

$watch和$digest是同一个硬币的俩面,他们一起承载消费周期的核心:反应数据的变化

通过$watch你可以添加一个watcher到scope对象上。当scope上有变化发生时watcher会收到通知。

你可以通过两个函数参数调用$watch 函数:

1.一个watch函数,可以指定你感兴趣的数据。
2.一个listener函数,当数据改变时会被调用。

在angular里,你通常会指定一个watch表达式来代替watch函数

一个watch表达式可以是一个字符串,像”user.firstName”,可以放在数据绑定,指令属性或者javascript代码里。

它在angualr内部被解析和编译成watch函数。

另外一个是$digest函数,它会遍历scope上所有的watchers,运行他们的watch和listener函数

Scope首先需要存储所有被注册的watchers:

1
2
3
4
5
6
7
8
9
10
11
function Scope() {
this.$$watchers = [];
}

Scope.prototype.$watch = function(watchFn,listenerFn){
var watcher = {
watchFn: watchFn,
listenerFn:listenerFn
};
this.$$watchers.push(watcher);
}

最后有个$digest函数,让我们定义个简单版本,只是遍历所有注册过的watchers,然后调用他们的listener函数。

1
2
3
4
5
Scope.prototype.$digest = function(){
_.foreach(this.$$watchers,function(watcher){
watcher.listenerFn();
});
};

我们也能看到angular scopes有重要的性能指标:
1.不是通过scope附加的data在性能上必须考虑。如果没有watcher监控属性,它就不关心是否是在scope上。angular不会遍历scope上的所有属性,它只会遍历watchers。
2.美一个watch函数在每一个$digest调用中被调用。基于这个原因,得考虑你拥有的watches,还有你的watch函数和表达式的性能。

javascript之__proto__

发表于 2017-07-28 | 更新于 2019-05-07

proto作用

用法

今天在看chalk源码时,发现这么一段话:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function build(_styles) {
var builder = function () {
return applyStyle.apply(builder, arguments);
};

builder._styles = _styles;
builder.enabled = this.enabled;
// __proto__ is used because we must return a function, but there is
// no way to create a function with a different prototype.
/* eslint-disable no-proto */
builder.__proto__ = proto;

return builder;
}

用proto是因为要返回一个方法,而且要带上一个不同的原型。

实验

我们用以下方法来做个测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var proto1 = defineProps(function chalk1() {}, {

'ss': {
get: function () {
return "ssssss";
}
}
});

console.log(proto1.ss);
function build(_styles) {
var builder = function () {

};

// __proto__ is used because we must return a function, but there is
// no way to create a function with a different prototype.
/* eslint-disable no-proto */
builder.__proto__ = proto1;
return builder;
}

var _b = build();
console.log(_b.ss);

输出:

1
2
ssssss
ssssss

参考资料:
create function in javascript with custom prototype
Is it possible to create a function with another prototype than Function.prototype?

angular-源码分析之isClass函数

发表于 2017-07-27 | 更新于 2019-05-07

isClass函数

isClass函数顾名思义是判断函数是否是类的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function stringifyFn(fn) {
// Support: Chrome 50-51 only
// Creating a new string by adding `' '` at the end, to hack around some bug in Chrome v50/51
// (See https://github.com/angular/angular.js/issues/14487.)
// TODO (gkalpak): Remove workaround when Chrome v52 is released
return Function.prototype.toString.call(fn) + ' ';
}
function isClass(func) {
// IE 9-11 do not support classes and IE9 leaks with the code below.
if (msie <= 11) {
return false;
}
// Support: Edge 12-13 only
// See: https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/6156135/
/*调用 stringifyFn字符串化后再用正则判断*/
return typeof func === 'function'
&& /^(?:class\b|constructor\()/.test(stringifyFn(func));
}

实验

1
2
3
4
5
6
7
8
class Person {
constructor(name) {
this.name=name||"Default";
}
}
console.log(stringifyFn(Person));
console.log(typeof Person === 'function');
console.log(isClass(Person));

输出

1
2
3
4
5
6
7
8
9
10
class Person {
constructor(name) {
this.name=name||"Default";
}
// toString(){
// return 'Name:'+this.name;
// }
}
true
true

angular-源码分析之rootScope

发表于 2017-07-27 | 更新于 2019-05-07

$rootScope 到底是啥

出处

在bootstrap函数中会调用injector.invoke,里面用到了rootScope:

1
2
3
4
5
6
7
8
injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',
function bootstrapApply(scope, element, compile, injector) {
scope.$apply(function() { //rootScope
element.data('$injector', injector);
compile(element)(scope);
});
}]
);

从之前的文章中我们可以知道$rootScope从$rootScopeProvider.$get方法返回的,那么我们就可以看$rootScopeProvider.$get方法返回了什么。

$RootScopeProvider

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
function Scope() {
this.$id = nextUid();
this.$$phase = this.$parent = this.$$watchers =
this.$$nextSibling = this.$$prevSibling =
this.$$childHead = this.$$childTail = null;
this.$root = this;
this.$$destroyed = false;
this.$$listeners = {};
this.$$listenerCount = {};
this.$$watchersCount = 0;
this.$$isolateBindings = null;
}

Scope.prototype = {
constructor: Scope,
$new: function(isolate, parent) {

},
//$apply方法就是在这定义的
//expr 可以为function
$apply: function(expr) {
try {
beginPhase('$apply');
try {
return this.$eval(expr);
} finally {
clearPhase();
}
} catch (e) {
$exceptionHandler(e);
} finally {
try {
$rootScope.$digest();
} catch (e) {
$exceptionHandler(e);
throw e;
}
}
}
//省略
};
//定义了Scope对象
var $rootScope = new Scope();

//The internal queues. Expose them on the $rootScope for debugging/testing purposes.
var asyncQueue = $rootScope.$$asyncQueue = [];
var postDigestQueue = $rootScope.$$postDigestQueue = [];
var applyAsyncQueue = $rootScope.$$applyAsyncQueue = [];

var postDigestQueuePosition = 0;

return $rootScope;
1…171819…47

John

232 日志
43 标签
GitHub Twitter
欢迎关注我的公众号:沉迷Spring
© 2023 johnwonder
由 Hexo 强力驱动 v3.2.0
|
主题 – NexT.Pisces v7.1.1
|
0%