We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
JavaScript 中的对象有一个特殊的 [[Prototype]] 内置对象。这个内置对象和原型(链)有关。
对于默认的 [[Get]] 操作来说,首先是检查对象本身是否有这个属性,如果有就使用它。假如没有这个属性,就要继续访问 [[Prototype]] 链,查看 [[Prototype]] 链上是否有这个属性([[Prototype]] 是一个对象,在 [[Prototype]] 上可能还存在 [[Prototype]] 内部属性)。
在 ECMAScript 规范中对 [[Prototype]] 内部属性描述:
所有对象都有一个叫做 [[Prototype]] 的内部属性。此对象的值是 null 或一个对象,并且它用于实现继承。一个原生属性是否可以把宿主对象作为它的 [[Prototype]] 取决于实现。所有 [[Prototype]] 链必须是有限长度(即,从任何对象开始,递归访问 [[Prototype]] 内部属性必须最终到头,并且值是 null)。从 [[Prototype]] 对象继承来的命名数据属性(作为子对象的属性可见)可以通过 get 获取,但无法用于 通过 put 写入。命名访问器属性会把 get 和 put 请求都继承。
从规范中我们可以发现:
其中第四、五点,在 **<<你不知道的 JavaScript(上卷)>>**第五章有描述:
如果在 [[Prototype]] 链上层存在名为 foo 的普通数据访问属性(参见第 3 章)并且没 有被标记为只读( writable:false ),那就会直接在 myObject 中添加一个名为 foo 的新 属性,它是屏蔽属性。 如果在 [[Prototype]] 链上层存在 foo ,但是它被标记为只读( writable:false ),那么 无法修改已有属性或者在 myObject 上创建屏蔽属性。如果运行在严格模式下,代码会 抛出一个错误。否则,这条赋值语句会被忽略。总之,不会发生屏蔽。 如果在 [[Prototype]] 链上层存在 foo 并且它是一个 setter(参见第 3 章),那就一定会 调用这个 setter。 foo 不会被添加到(或者说屏蔽于) myObject ,也不会重新定义 foo 这 个 setter。
var obj = {}; Object.defineProperty(obj, 'a', { writable: true, value: 1 }); var res = Object.create(obj); // create 函数后面介绍 console.log(res.a); // 1 console.log(res); {} res.a = 10; console.log(res.a); // 10 console.log(res); {a: 10}
obj 对象的 a 属性的属性描述符 writable 为 true 时,会直接在 obj 对象本身添加 a 属性。如果 writable 为 false,会发生屏蔽,但是能取值。
var obj = {}; Object.defineProperty(obj, 'a', { writable: false, value: 1 }); var res = Object.create(obj); console.log(res.a); // 1 console.log(res); {} res.a = 10; console.log(res.a); // 1 console.log(res); {}
如果 [[Prototype]] 链上有存取描述符中的 setter,会调用 setter 函数。
var obj = {}; var val = 1; Object.defineProperty(obj, 'a', { get: function() { return val; }, set: function (value) { val = value; } }); var res = Object.create(obj); console.log(res.a); // 1 res.a = 10; console.log(res.a); // 10 console.log(val); // 10
对于是否发生屏蔽以及是否调用 setter ,在 ECMAScript 规范中的**简单赋值 = **已经规定好了,上面只是把规定表现出来了。
Object.create 函数很常见,作用就是按照原型创建一个新对象。
Object.create ( O [, Properties] ) create 函数按照指定的原型创建一个新对象。当调用 create 函数,采用如下步骤: 如果 Type(O) 不是 Object 或 Null,则抛出一个 TypeError 异常。 令 obj 为 new Object() 创建新对象的结果,这里的 Object 是标准内置构造器名。 设定 obj 的 [[Prototype]] 内部属性为 O。 如果传入了 Properties 参数并且不是 undefined,则用 obj 和 Properties 当作参数调用标准内置函数 Object.defineProperties 一样给 obj 添加自身属性。 返回 obj。
create 函数按照指定的原型创建一个新对象。当调用 create 函数,采用如下步骤:
过程很简单,new Object() 创建一个新对象 obj,并设置 obj 的 [[Prototype]] 属性为 O。
所以只要设置了 [[Prototype]] 内部属性,就可以实现继承。
var proto = { b: 10 }; var res = Object.create(proto, { foo: { value: 1 } }); console.log(res) // {foo: 1, __proto__: {b: 10, ...}}
在函数章节介绍了 new ,这里关注 [[Prototype]] 相关的部分。
我们知道 new 运算符 最后会调用函数对象的 [[Construct]] 内置方法。
[[Construct]]
当以一个可能的空的参数列表调用函数对象 F 的 [[Construct]] 内部方法,采用以下步骤: 令 obj 为新创建的 ECMAScript 原生对象。 依照 8.12 设定 obj 的所有内部方法。 设定 obj 的 [[Class]] 内部属性为 "Object"。 设定 obj 的 [[Extensible]] 内部属性为 true。 令 proto 为以参数 "prototype" 调用 F 的 [[Get]] 内部属性的值。 如果 Type(proto) 是 Object,设定 obj 的 [[Prototype]] 内部属性为 proto。 如果 Type(proto) 不是 Object,设定 obj 的 [[Prototype]] 内部属性为 15.2.4 描述的标准内置的 Object 原型对象。 以 obj 为 this 值,调用 [[Construct]] 的参数列表为 args,调用 F 的 [[Call]] 内部属性,令 result 为调用结果。 如果 Type(result) 是 Object,则返回 result。 返回 obj。
当以一个可能的空的参数列表调用函数对象 F 的 [[Construct]] 内部方法,采用以下步骤:
只看第五、六、七点。
六七点都是设置新创建对象 obj 的 [[Prototype]]。如果 proto 不是对象,就设置为 标准内置 Object 原型对象。如果 proto 是对象,设置 [[Prototype]] 为 proto。
proto 是从第五点得来的:函数对象 F 的 "prototype" 属性。
回到创建函数对象步骤,我们只关注以下部分:
令 proto 为仿佛使用 new Object() 表达式创建新对象的结果,其中 Object 是标准内置构造器名。 以参数 "constructor"、属性描述符 {[[Value]]: F, { [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true}、false 调用 proto 的 [[DefineOwnProperty]] 内部方法。 以参数 "prototype"、属性描述符 {[[Value]]: proto, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false}、false 调用 F 的 [[DefineOwnProperty]] 内部方法。
第一点,proto 是通过 new Object() 创建的对象。
第二点,在 proto 对象上添加 constructor 属性,属性描述符中的 [[Value]] 值为函数对象。
第三点,在函数对象上添加 "prototype" 属性,属性描述符中的 [[Value]] 值为 proto 对象。
new 运算符创建的对象的 [[Prototype]] 被设置为函数对象的 prototype 属性的值,而函数对象 prototype 属性值又是通过 new Object() 得到的。
new Object() 也会新建一个对象,对象的 [[Prototype]] 内部属性为标准内置的 Object 的 prototype 对象 (15.2.4)。也就是我们熟悉的 constructor、**toString **、**toLocaleString **、**valueOf **、hasOwnProperty、**isPrototypeOf **、**propertyIsEnumerable **。constructor 在创建函数对象中会被屏蔽,设置为创建的函数对象。
所以 new 运算符创建的对象可以使用 **toString **、**valueOf **、hasOwnProperty 等方法。
函数对象的 [[Prototype]] 内部属性和 new 运算符没有任何关系。
在创建函数对象时,会内置 [[Prototype]] 属性的值:
设定 F 的 [[Prototype]] 内部属性为 15.3.3.1 指定的标准内置 Function 对象的 prototype 属性。
将 [[Prototype]] 内部属性值设为 Function 对象的 prototype 属性。Function 对象的 prototype 属性的值是我们常见的 constructor 、**toString **、**apply **、**call **、**bind **,所以函数对象可以使用这些方法。
引用 <<你不知道的 JavaScript (上卷)>>中的代码
function Foo(name) { this.name = name; } Foo.prototype.myName = function () { return this.name; } function Bar(name, label) { Foo.call(this, name); this.label = label; } Bar.prototype = Object.create(Foo.prototype); Bar.prototype.constructor = Bar; Bar.prototype.myLabel = function () { return this.label; } var a = new Bar('a', 'obj a'); a.myName(); // 'a' a.myLabel(); // 'obj a'
网上有这样一个例子:
Object instanceof Object // true Function instanceof Function // true Function instanceof Object // true Object instanceof Function // true
答案也给出了:
先有的Object.prototype, Object.prototype构造出Function.prototype,然后Function.prototype构造出Object和Function。 Object.prototype是鸡,Object和Function都是蛋。
ECMAScript 英文文档
ECMAScript 维基百科 中文文档
继承与原型链
Object/Function
<<你不知道的 JavaScript (上卷)>>
The text was updated successfully, but these errors were encountered:
No branches or pull requests
原型(链)
[[Prototype]]
JavaScript 中的对象有一个特殊的 [[Prototype]] 内置对象。这个内置对象和原型(链)有关。
对于默认的 [[Get]] 操作来说,首先是检查对象本身是否有这个属性,如果有就使用它。假如没有这个属性,就要继续访问 [[Prototype]] 链,查看 [[Prototype]] 链上是否有这个属性([[Prototype]] 是一个对象,在 [[Prototype]] 上可能还存在 [[Prototype]] 内部属性)。
在 ECMAScript 规范中对 [[Prototype]] 内部属性描述:
从规范中我们可以发现:
其中第四、五点,在 **<<你不知道的 JavaScript(上卷)>>**第五章有描述:
obj 对象的 a 属性的属性描述符 writable 为 true 时,会直接在 obj 对象本身添加 a 属性。如果 writable 为 false,会发生屏蔽,但是能取值。
如果 [[Prototype]] 链上有存取描述符中的 setter,会调用 setter 函数。
对于是否发生屏蔽以及是否调用 setter ,在 ECMAScript 规范中的**简单赋值 = **已经规定好了,上面只是把规定表现出来了。
Object.create
Object.create 函数很常见,作用就是按照原型创建一个新对象。
过程很简单,new Object() 创建一个新对象 obj,并设置 obj 的 [[Prototype]] 属性为 O。
所以只要设置了 [[Prototype]] 内部属性,就可以实现继承。
new 运算符
在函数章节介绍了 new ,这里关注 [[Prototype]] 相关的部分。
我们知道 new 运算符 最后会调用函数对象的 [[Construct]] 内置方法。
[[Construct]]
只看第五、六、七点。
六七点都是设置新创建对象 obj 的 [[Prototype]]。如果 proto 不是对象,就设置为 标准内置 Object 原型对象。如果 proto 是对象,设置 [[Prototype]] 为 proto。
proto 是从第五点得来的:函数对象 F 的 "prototype" 属性。
回到创建函数对象步骤,我们只关注以下部分:
第一点,proto 是通过 new Object() 创建的对象。
第二点,在 proto 对象上添加 constructor 属性,属性描述符中的 [[Value]] 值为函数对象。
第三点,在函数对象上添加 "prototype" 属性,属性描述符中的 [[Value]] 值为 proto 对象。
new 运算符创建的对象的 [[Prototype]] 被设置为函数对象的 prototype 属性的值,而函数对象 prototype 属性值又是通过 new Object() 得到的。
new Object() 也会新建一个对象,对象的 [[Prototype]] 内部属性为标准内置的 Object 的 prototype 对象 (15.2.4)。也就是我们熟悉的 constructor、**toString **、**toLocaleString **、**valueOf **、hasOwnProperty、**isPrototypeOf **、**propertyIsEnumerable **。constructor 在创建函数对象中会被屏蔽,设置为创建的函数对象。
所以 new 运算符创建的对象可以使用 **toString **、**valueOf **、hasOwnProperty 等方法。
函数对象的 [[Prototype]]
函数对象的 [[Prototype]] 内部属性和 new 运算符没有任何关系。
在创建函数对象时,会内置 [[Prototype]] 属性的值:
将 [[Prototype]] 内部属性值设为 Function 对象的 prototype 属性。Function 对象的 prototype 属性的值是我们常见的 constructor 、**toString **、**apply **、**call **、**bind **,所以函数对象可以使用这些方法。
常见的继承
引用 <<你不知道的 JavaScript (上卷)>>中的代码
Object/Function
网上有这样一个例子:
答案也给出了:
参考资料
ECMAScript 英文文档
ECMAScript 维基百科 中文文档
继承与原型链
Object/Function
<<你不知道的 JavaScript (上卷)>>
The text was updated successfully, but these errors were encountered: