多态:父类的一些通用行为可以被子类的行为重写。
实例化对象就是类的所有特性的一份副本
。[[Prototype]]
[[Prototype]]
内置属性,这其实是对其他对象的引用。在所有对象创建时 [[Prototype]]
属性都会被赋予一个非空的值。var myObject = {
a: 2
};
myObject.a; // 2
[[Prototype]]
引用有什么用呢?在之前的文章中我们说过,当视图引用对象的属性时会触发 [[Get]] 操作,比如 myObject.a
。对于默认的 [[Get]] 操作来说,第一步是检查对象本身是否有这个属性,如果有的话就使用它。但如果 a
不在 myObject
中,就需要使用对象的 [[Prototype]]
原型链了。var anotherObject = {
a: 2
};
var myObject = Object.create(anotherObject);
myObject.a; // 2
Object.create()
方法,我们可以暂且先不考虑它,后续会聊到。myObject
对象的 [[Prototype]]
关联到了 anotherObject
上。显然 myObject.a
并不存在,但是属性访问仍然能成功地(在 anotherObject
) 找到了 a
。anotherObject
中也找不到 a
并且 [[Prototype]]
链不为空的话,就会继续查找下去。[[Prototype]]
链。如果找到完整的 [[Prototype]]
链的话,[[Get]] 操作的就会返回 undefined。
for...in
遍历对象和 in
操作符时都会查找对象的整条原型链
。(无论属性是否可枚举)var anotherObject = {
a:2
};
// 创建一个关联到 anotherObject 的对象
var myObject = Object.create( anotherObject );
for (var k in myObject) {
console.log("found: " + k);
}
// found: a
("a" in myObject); // true
当你通过各种语法进行属性查找时都会查找
[[Prototype]]链,直到找到属性或找到完整的原型链。
[[Prototype]]
的尽头呢?[[Prototype]]
链最终都会指向内置的 Object.prototype
。myObject.foo = 'bar';
myObject
中存在 foo
属性,那以上这条赋值语句只会修改已有的属性值。myObject
中不存在 foo
属性,[[Prototype]]
原型链就会遍历查找,类似于 [[Get]]
操作,如果原型链上找不到 foo
, foo
就会被添加到 myObject
上。foo
存在于原型链的上层,以上赋值语句的行为就会有些不同,后续会聊到。foo
属性即存在于 myObject
中,也出现在 myObject
的 [[Prototype]]
原型链上层,那就会发生屏蔽
。myObject
中的 foo
属性会屏蔽原型链上层的所有 foo
属性,因为 myObject.foo
总会选择原型链中最底层的 foo
属性。let a = {
foo: "afoo",
};
let c = Object.create(a);
c.foo = "cfoo";
console.log("c ------>", c.foo); // cfoo
myObject.foo = 'bar';
会出现三种情况:[[Prototype]]
原型链上层存在 foo 访问属性,并且没有被标记为只读(writable: false)
,那就会直接在 myObject 中添加一个 foo 属性,则它是屏蔽属性。[[Prototype]]
原型链上存在 foo 属性,但是被标记为只读, 那就无法修改已有属性或在 myObject 上创建屏蔽属性。如果在严格模式下运行,会直接抛出一个错误。否则,这条赋值语句就会被忽略。总之,不会发生屏蔽。[[Prototype]]
原型链上层存在 foo 并且它是一个 setter
,那就一定会调用这个 setter
。foo 不会被添加到(可以说屏蔽到) myObject 中,也不会重新定义 foo 这个 setter
。如下代码:let a = {
get foo() {
return this._foo_;
},
set foo(v) {
this._foo_ = 'afoo';
},
};
a.foo = 'afoo';
let c = Object.create(a);
c.foo = "cfoo";
console.log("c ------>", c.foo); // afoo
// 把赋值[[put]] 操作存储到了另一个变量 _a_ 中,名称 _a_ 只是一种惯例,没有任何特殊行为,与其他普通属性一样。
[[Prototype]]
链上层已经存在的属性进行 [[Put]] 赋值操作,就一定会触发屏蔽,但如你所见,三种情况中只有第一种是这样的。=
操作符来赋值,而是使用 Object.defineProperty(...)
来向 myObject 中添加 foo。[[Prototype]]
原型链下层隐式屏蔽同名属性。而这个限制仅存在于 =
操作符中,使用 Object.defineProperty(...)
则不会受到影响。类可以或实例化多次
。function Foo(){
//
};
var a = new Foo();
Object.getPrototypeof(a) === Foo.prototype; // true
在对象中,继承意味着复制操作
,JavaScript 默认情况下不会复制对象属性,只会在两个对象之间创建一个关联。function Foo(){
//
}
var a = new Foo();
new
,在面向对象的语言中构造类实例时也会用到它。另一个原因就是,看起来我们执行了类的构造函数方法,而 Foo() 的调用方式很像初始化类时构造函数的调用方式。function Foo(){ }
Foo.prototype.constructor === Foo; // true
var a = new Foo();
a.constructor === Foo(); // true
// 实际上 .constructor 引用同样被委托给了 Foo.prototype,而 Foo.prototype.constructor 默认指向 Foo
constructor 属性引用的是对象关联的函数
(上述代码中是 Foo)。类 名首字母要大写
,所以 Foo 而非 foo,这也提示这它是一个 类
。Foo 和普通函数没有任何区别
。函数本身并不是构造函数
。但是当你在普通的函数调用前加上 new 关键字后,就会把当前函数编程一个构造函数调用
。实际上,new 会劫持所有普通函数并用构造对象的形式来调用它
。function NothingSpecial() {
console.log( "Don't mind me!" );
}
var a = new NothingSpecial();
// "Don't mind me!"
a; // {}
在 JavaScript 中对于构造函数最准确的解释是,所有带 new 的函数调用。
所以,函数不是构造函数,但是仅当使用 new 时,函数调用就会被变成 构造函数调用。[[Prototype]]
机制就是存在于对象中的一个内部链接,他会引用到其他对象。[[Prototype]]
关联的对象进行查找。如果后者也没有找到需要的引用就会继续查找它的 [[Prototype]]
,直到 Object.prototype
为止。以此类推,这一系列的对象链接被称为 "原型链"。[[Prototype]]
机制的意义是什么?为什么要创建这些关联呢?var foo = {
something: function(){
console.log('do something');
}
};
var bar = Object.create(foo);
var.something(); // do something
Object.create() 创建了一个新对象且关联到 foo
,这样就可避免一些不必要的麻烦(比如使用 new 的构造函数调用会生成 .prototype 和 .constructor 引用)Object.create(null) 会创建一个空链接的对象,因为是空的,所有无法进行委托,并且由于这个对象没有原型链,在使用 instanceof 时也就无法进行判断,因此他们总是会返回 false。
// Object.create()的 polyfill 代码
if(Object.create){
Object.create = function(o){
function F(){};
F.prototype = o;
return new F();
}
}
var anotherObject = {
a:2
};
var myObject = Object.create( anotherObject, {
b: {
enumerable: false,
writable: true,
configurable: false,
value: 3
},
c: {
enumerable: true,
writable: false,
configurable: false,
value: 4
}
});
myObject.hasOwnProperty( "a" ); // false
myObject.hasOwnProperty( "b" ); // true
myObject.hasOwnProperty( "c" ); // true
myObject.a; // 2
myObject.b; // 3
myObject.c; // 4
[[Prototype]]
关联的对象,这个关联关系就是一条 "原型链"(有点像嵌套的作用域),在找到属性时会对它进行遍历。[[Prototype]]
链关联的。[[Prototype]]
原型链上层存在对象中的属性访问属性,并且没有被标记为只读(writable: false)
,那就会直接在当前对象中添加一个对象中的属性属性,则它是屏蔽属性。[[Prototype]]
原型链上存在对象中的属性属性,但是被标记为只读, 那就无法修改已有属性或在当前对象上创建屏蔽属性。如果在严格模式下运行,会直接抛出一个错误。否则,这条赋值语句就会被忽略。总之,不会发生屏蔽。[[Prototype]]
原型链上层存在对象中的属性并且它是一个 setter
,那就一定会调用这个 setter
。对象中的属性不会被添加到(可以说屏蔽到)当前对象中,也不会重新定义对象中的属性这个 setter
。for...in
遍历对象和 in
操作符时都会查找对象的整条原型链
。(无论属性是否可枚举)实例化对象就是类的所有特性的一份副本
。•问题标注 Q:(question)
•答案标注 R:(result)
•注意事项标准:A:(attention matters)
•详情描述标注:D:(detail info)
•总结标注:S:(summary)
•分析标注:Ana:(analysis)
•提示标注:T:(tips)