sailei1 / blog

1 stars 0 forks source link

你不知道的JS -原型笔记 #73

Closed sailei1 closed 5 years ago

sailei1 commented 5 years ago

JavaScript 中的对象有一个特殊的 [[Prototype]] 内置属性,其实就是对于其他对象的引用 Object.create(..) 创建一个对象并把这个对象的 [[Prototype]] 关联到指定的对象。 所有普通的 [[Prototype]] 链最终都会指向内置的 Object.prototype。 所有普通对象都有内置的 Object.prototype,指向原型链的顶端(比如说全局作用域),如果在原型链中找不到指定的属性就会停止。toString()、valueOf() 和其他一些通用的功能都存在于 Object.prototype 对象上,因此语言中所有的对象都可以使用它们。

myObject.foo = "bar”; 设置 如果 myObject 对象中包含名为 foo 的普通数据访问属性,这条赋值语句只会修改已有的属 性值。 如果 foo 不是直接存在于 myObject 中,[[Prototype]] 链就会被遍历,类似 [[Get]] 操作。 如果原型链上找不到 foo,foo 就会被直接添加到 myObject 上。 屏蔽 如果属性名 foo 既出现在 myObject 中也出现在 myObject 的 [[Prototype]] 链上层,那么就会发生屏蔽。myObject 中包含的 foo 属性会屏蔽原型链上层的所有 foo 属性,因为myObject.foo 总是会选择原型链中最底层的 foo 属性。

如果 foo 不直接存在于 myObject 中而是存在于原型链上层时 myObject.foo = "bar" 会出现的三种情况

  1. 如果在[[Prototype]]链上层存在名为foo的普通数据访问属性(参见第3章)并且没有被标记为只读(writable:false),那就会直接在 myObject 中添加一个名为 foo 的新属性,它是屏蔽属性。
  2. 如果在[[Prototype]]链上层存在foo,但是它被标记为只读(writable:false),那么无法修改已有属性或者在 myObject 上创建屏蔽属性。如果运行在严格模式下,代码会抛出一个错误。否则,这条赋值语句会被忽略。总之,不会发生屏蔽。
  3. 如果在[[Prototype]]链上层存在foo并且它是一个setter(参见第3章),那就一定会调用这个 setter。foo 不会被添加到(或者说屏蔽于)myObject,也不会重新定义 foo 这个 setter。
    
    var anotherObject = { 
      a:2
    };
    var myObject = Object.create( anotherObject );
     anotherObject.a; // 2
     myObject.a; // 2
     anotherObject.hasOwnProperty( "a" ); // true
     myObject.hasOwnProperty( "a" ); // false
     myObject.a++; // 隐式屏蔽!
     anotherObject.a; // 2
     myObject.a; // 3
     myObject.hasOwnProperty( "a" ); // true

//修改委托属性时一定要小心。如果想让 anotherObject.a 的值增加,唯一的办法是anotherObject.a++。

function Foo() { // ... } var a = new Foo(); Object.getPrototypeOf( a ) === Foo.prototype; // true

在 JavaScript 中,并没有类似的复制类机制。你不能创建一个类的多个实例,只能创建 多个对象,它们 [[Prototype]] 关联的是同一个对象。但是在默认情况下并不会进行复制, 因此这些对象之间并不会完全失去联系,它们是互相关联的。
new Foo() 会生成一个新对象(我们称之为 a),这个新对象的内部链接 [[Prototype]] 关联 的是 Foo.prototype 对象。
最后我们得到了两个对象,它们之间互相关联,就是这样。我们并没有初始化一个类,实 际上我们并没有从“类”中复制任何行为到一个对象中,只是让两个对象互相关联。

**constructor**

function Foo() { // ... } Foo.prototype.constructor === Foo; // true var a = new Foo(); a.constructor === Foo; // true

//a 本身并没有 .constructor 属性。而且,虽然 a.constructor 确实指 向 Foo 函数,但是这个属性并不是表示 a 由 Foo“构造”
//.constructor 引用同样被委托给了 Foo.prototype,而Foo.prototype.constructor 默认指向 Foo。
在 JavaScript 中对于“构造函数”最准确的解释是,所有带 new 的函数调用。

function NothingSpecial() {console.log( "Don't mind me!" ); } var a = new NothingSpecial(); // "Don't mind me!” a; // {} new 的副作用


原型继承

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 对象并关联到 Foo.prototype Bar.prototype = Object.create(Foo.prototype); // 注意!现在没有 Bar.prototype.constructor 了 // 如果你需要这个属性的话可能需要手动修复一下它 Bar.prototype.myLabel = function() { return this.label; }; var a = new Bar( "a", "obj a" ); a.myName(); // "a" a.myLabel(); // "obj a"

polyfill object.create(…)

Object.create = function(o) { function F(){} F.prototype = o; return new F(); };

//Object.create(null) 会 创 建 一 个 拥 有 空( 或 者 说 null)[[Prototype]]链接的对象,这个对象无法进行委托。由于这个对象没有原型链,所以instanceof 操作符(之前解释过)无法进行判断,因此总是会返回 false
检查 “类”关系
a instanceof Foo //true
Foo.prototype.isPrototypeOf( a ); // true

对象关联
如果在对象上没有找到需要的属性或者方法引用,引擎就会继续在 [[Prototype]] 关联的对象上进行查找。同理,如果在后者中也没有找到需要的引用就会继续查找它的 [[Prototype]],以此类推。这一系列对象的链接被称为“原型链”。
关联关系是备用

var anotherObject = {cool: function() { console.log( "cool!" ); } }; var myObject = Object.create( anotherObject ); myObject.cool(); // "cool!” //由于存在 [[Prototype]] 机制,这段代码可以正常工作。但是如果你这样写只是为了让myObject 在无法处理属性或者方法时可以使用备用的 anotherObject,那么你的软件就会变得有点“神奇”,而且很难理解和维护。 var anotherObject = {cool: function() { console.log( "cool!" ); } }; var myObject = Object.create( anotherObject ); myObject.doCool = function() {this.cool(); // 内部委托! }; myObject.doCool(); // "cool!"