《你不知道的javascript》原型

Surahe      2022-02-06     177

关键词:

1 [[Prototype]]

[[Prototype]]是对其他对象的引用,几乎所有对象在创建时[[Prototype]]属性会被赋予非空值。

var myObject = {
      a:2
}

myObject.a;  //  2  

 

引用对象属性时会触发[[Get]]操作,它会检查对象本身是否有这个属性,如果有就使用它,但a不在myObject,需要使用对象的[[Prototype]]链。

使用for in遍历对象时原理和查找[[Prototype]]链类似,任何可以通过原型链访问到的属性都会被枚举。使用in操作符来检查属性在对象是否存在也会查找对象的整条原型链。

var anotherObject = {
      a:2
}

var myObject = Object.create( anotherObject  );

for(var k in myObject){
    console.log( k );
}

 

 

1.1 Object.prototype

所有普通的[[Prototype]]链最终都指向内置的Object.prototype。由于所有的“普通”(内置,不少特定主机的扩展)对象都源于这个Object.prototype,所以它包含JavaScript许多通用功能。

1.2 属性设置和屏蔽

myObject.foo = "bar";

如果foo不是直接存在于myObject,[[Prototype]]链会被遍历,如果原型链找不到foo,foo会被添加到myObject。

如果foo不直接存在于myObject而是存在于原型链上层时:

  1. 如果在[[Prototype]]链上层存在名为foo的普通数据访问属性并且没有被标记为只读(writeable:false),直接在myObject添加foo属性,它是屏蔽属性
  2. 如果在[[Prototype]]链上存在foo,但它被标记为只读,将无法修改已有属性或在myObject上创建屏蔽属性。在严格模式中会抛出错误,否则忽略此语句。
  3. 如果在[[Prototype]]链上存在foo且它是一个setter,那一定会调用这个setter。foo不会被添加到myObject,也不会重新定义foo。

如果细微第二种和第三种情况也会屏蔽foo,不能使用=赋值,而是使用Object.defineProperty()向myObject添加foo。

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

 

++操作相当于myObject.a = myObject.a + 1。++操作先会通过[[Prototype]]查找属性a并从anotherObject.a获取当前属性值2,然后给它加1,接着用[[Put]]将3赋值给myObejct的新建屏蔽属性a。

如果想让anotherObjecg的值增加,唯一的办法是anotherObject.a++。

2 "类"

2.1 "类"函数

function Foo() {}

Foo.prototype;  //  {}

 

这个对象在调用new Foo()时创建,最后被关联到“Foo点prototype”。

 

function foo() {}

var a = new foo();

foo.prototype == Object.getPrototypeOf(a)  //  true

 

调用new foo()创建a,其中一步是将a的[[Prototype]]连接到foo.prototype所指向的对象

 

 

2.2 “构造函数”

function Foo() {}

Foo.prototype.constructor === Foo;  // true

var a = new Foo();

a.constructor === Foo;  //  true

 

Foo.prototype默认有一个公有且不可枚举属性.constructor,它引用的是对象关联的函数(Foo)。通过“构造函数”调用new Foo()创建的对象也有一个.constructor属性,指向“创建这个对象的函数”。

 

当在普通的函数调用前面加上new后,就会把这个函数调用变成一个“构造函数调用”。new会劫持普通函数并用构造对象的形式聊调用它。

function NothingSpecial() {
    console.log("Don‘t mind me");
}

var a = new NothingSpecial();

a; // {}

 

NothingSpecial只是普通函数,但使用new调用时,它会构造一个对象并赋值给 a

在JavaScript对“构造函数”最准确的解释是,所有带new的函数调用。

2.3 技术 

a.constructor === Foo为真,不代表a有指向Foo的.constructor属性。.constructor引用同样被委托给Foo.prototype,foo.prototype.constructor默认指向foo。a.constructor只是通过默认[[prototype]]委托指向Foo。

Foo.prototype的.constructor属性只是Foo函数在声明时的默认属性。如果你创建一个新对象并替换了函数默认的.prototype对象引用,新对象不会自动获得.constructor属性。

function Foo() { /*...*/ }

Foo.prototype = { /*...*/ }

var a1 = new Foo();

a1.constructor === Foo;  //  false
a1.constructor === true  //  true

 

a1没有.constructor属性,它会委托[[Prototype]]链上的Foo.prototype。但这个对象也没.constructor属性(已经被修改),所以它会继续委托,委托给了委托链顶端的Object.prototype。这个对象的.constructor属性指向内置的Object函数。

3 (原型)继承

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"

 

调用Object.create()会凭空创建一个“新”对象并把新对象内部的[[Prototype]]关联到你指定的对象(Foo.prototype)。

Bar.prototype = Object.create(Foo.prototype) 的意思是“创建一个新的Bar.prototype对象并把它关联到Foo.prototype”。

 

//错误做法

//  和想要的机制不一样
Bar.prototype = Foo.prototype;

//可能产生副作用
Bar.prototype = new Foo();

 

Bar.prototype = Foo.prototype 只是让Bar.prototype直接引用Foo.prototype对象。因此执行如Bar.prototype.mylabel = ...的赋值语句会直接修改Foo.prototype对象本身。

Bar.prototype = new Foo() 会创建关联到Bar.prototype的新对象,但它使用的是构造函数调用,如果函数Foo有一些副作用(如写日志、修改状态、注册到其他对象、给this添加数据属性等),就会影响到Bar()的“后代”。

 

检查“类关系

在传统面向类环境,检查一个实例(JavaScript的对象)的继承祖先(JavaScript的委托关系)通常被成为内省(或者反射)。

function Foo( ) {
    //  ...
}

Foo.prototype.blash = ...;

var a = new Foo();

 

第一种方法: a instanceof Foo ;  //  true

instanceof 判断a的整条[[Prototype]]链是否有指向 Foo.prototype的对象。但这个方法只能处理对象和函数的关系。

第二种方法: Foo.prototype.isPropertyOf( a );  //  true 

4 对象关联

 [[Prototype]]机制是存在于对象的一个内部链接,它会引用其他对象。通常它的作用是:如果在对象上没有找到需要的属性或方法引用,引擎就会继续在 [[Prototype]]关联的对象上查找。这一系列对象的链接被称为“原型链”。

4.1创建关联

var foo = {
    something: function() {
        console.log("Tell me someting goos.");
    }
}

var bar = Object.create( foo );

bar.something( );   //  Tell me someting goos.

 

Object.create会创建一个新对象并把它关联到指定的对象(foo)。

Object.create(null)会创建一个拥有空(或者说null)[[Prototype]]链接的对象,这个对象无法进行委托。因为他没有原型链,所以instanceof无法进行判断,总返回false。这些特殊的空[[Prototype]]对象通常被称为“字典”,它们完全不受原型链影响,非常适合存储数据

 

Object.create的polyfill代码

Object.create是ES5新增的函数,在ES5前的环境要支持这个功能要使用这段代码,它实现了Obejct.create的部分功能

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

 

这段代码使用了一个一次性函数F,通过改写它的.prototype使其指向想要关联的对象,然后再使用new F()构造一个新对象进行关联。Object.create的第二个参数指定需要添加到新对象的属性名和这些属性的属性描述符,但ES5之前的版本无法模拟属性描述符。

 

4.2 关联关系是必备的

假设要调用myObject.cool(),如果myObject中不存在cool()也能正常工作,这API会很“神奇”。

var anotherObject = {
    cool: function() {
        console.log("cool!");
    }
}

var myObject = Object.create( anotherObject );

myObject.cool( );   //  cool!

 

 

var anotherObject = {
    cool: function() {
        console.log("cool.");
    }
}

var myObject = Object.create( anotherObject );

myObject.doCool = function() {
    this.cool();  //  内部委托
}

myObject.doCool( );  

 

 这里强调的myObject.doCool()是实际存在于myObject的,让API设计更加清晰。从内部来说,这种实现遵循的是委托设计模式,通过[[Prototype]]委托到anotherObject.cool()。

 

内部委托比起直接委托可以让API接口设计更加清晰。

 

《你不知道的javascript》——this和对象原型

 《你不知道的javascript》【3】——this和对象原型https://www.bilibili.com/video/BV1iE411P7UP 浅显的总结《你不知道的js》this指向          右查找的副作用:查找到顶层都找不到,就会抛出 查看详情

你不知道的javascript(上卷)小结

上卷主要讲了作用域、闭包、this以及原型方面的内容。整体在github上瞥了一眼了原版的ydkjs,到目前修改篇幅有点大了,this和原型部分的目录已经不见了,应该是改动不少。说说本书的小缺点,一是,有些东西已经和实际不一... 查看详情

读书笔记《你不知道的javascript(上卷)》——第二部分this和对象原型(代码片段)

文章目录第6章行为委托6.1面向委托的设计6.1.1类理论6.1.2委托理论1.互相委托(禁止)2.调试6.1.3比较思维模型6.2类与对象6.2.1控件“类”ES6的class语法糖6.2.2委托控件对象6.3更简洁的设计反类6.4更好的语法反词法6... 查看详情

你不知道的javascript笔记

this和对象原型this是一个很特别的关键字,被自动定义在所有函数的作用域中//foo.count是0,字面理解是错误的    functionfoo(num){        console.log("foo:"+num);    &n 查看详情

你不知道的javascript-上卷の读书笔记

...;        1— 作用域对JavaScript而言,大部分情况下编译发生 查看详情

你不知道的js5-原型

1、原型[[prototype]]js中的对象有一个特殊的[[prototype]]内置属性,其实就是对于其他对象的引用,几乎所有的对象在创建时[[prototype]]属性都会被赋予一个非空的值使用for..in和in操作符都会查找对象的整条原型链所有普通的[[prototype]... 查看详情

《你不知道的javascript》系列分享专栏

《你不知道的JavaScript》系列分享专栏你不知道的JavaScript”系列就是要让不求甚解的JavaScript开发者迎难而上,深入语言内部,弄清楚JavaScript每一个零部件的用途《你不知道的JavaScript》已整理成PDF文档,点击可直接下载至本地查... 查看详情

《你不知道的javascript[中卷]》14——asynquence附录

 《你不知道的JavaScript[中卷]》【14】——asynquence附录 查看详情

你不知道的javascript(中卷)笔记

<!DOCTYPEhtml><html><head><metacharset="utf-8"><title>你不知道的javascript(中卷)</title></head><body><scripttype="text/javascript">/*//封装对象包装vara=newBool 查看详情

javascript你不知道的事儿

if(in)语句letnames=['Lily','Barry','Dendi','Boogie','Lily'];letnameNum=names.reduce((pre,cur)=>{if(curinpre){//pre中是否有cur属性pre[cur]++;}else{pre[cur]=1;//为pre这个对象添加cur属性,并且赋值为1}returnpre;},{ 查看详情

你不知道的javascript之类型

JavaScript是一门简单易用的语言,应用广泛,同时它的语言机制又十分复杂和微妙,即使经验丰富的开发人员也需要用心学习才能真正掌握。《你不知道的JavaScript》中是这样定义类型的:类型是值的内部特征,它定义了值的行为... 查看详情

你不知道的javascript(上卷卷)笔记

<!DOCTYPEhtml><html><head><metacharset="utf-8"><title>你不知道的javascript(上卷)</title></head><body></body></html>   查看详情

你不知道的javascript:有趣的settimeout

你不知道的Javascript:有趣的setTimeout有时候,小小的细节往往隐藏着大大的智慧今天在回顾JavaScript进阶用法的时候,发现一个有趣的问题,话不多说,先上代码:for(varj=0;j<10;j++){setTimeout(function(){console.log(j)},5000)}看到这三行代... 查看详情

《你不知道的javascript》整理——this

最近在读一本进阶的JavaScript的书《你不知道的JavaScript(上卷)》,这次研究了一下“this”。当一个函数被调用时,会创建一个活动记录(执行上下文)。这个记录会包含函数在哪里被调用(调用栈)、函数的调用方法、... 查看详情

你不知道的javascript笔记

类型:JavaScript有7种内置类型空值(null)未定义(undefined)布尔值(boolean)数字(number)字符串(string)对象(object)符号(symbol)   除对象以外,其他统称为“基本类型” 用typeof运算符来查看值的类型typeofundefined ==="undefi... 查看详情

《你不知道的javascript》整理——作用域提升与闭包

最近在读一本进阶的JavaScript的书《你不知道的JavaScript(上卷)》,里面分析了很多基础性的概念。可以更全面深入的理解JavaScript深层面的知识点。 一、函数作用域1)函数作用域就是作用域在一个“Function”里,属于... 查看详情

《你不知道的javascript》整理——对象

...造形式varmyObj=newObject();myObj.key=value; 二、类型对象是JavaScript的基础。1)基本类型在JavaScript中一共有六种主要类型(术语是“语言类型”):string、number、boolean、null、unde 查看详情

《你不知道的javascript》——闭包(代码片段)

第一部分还有最后的闭包没有讲...:1.非常浅显的阅读《你不知道的JavaScript》第一部分作用域与闭包https://www.bilibili.com/video/BV1jE411j7PU 从22分钟开始讲闭包              &nb 查看详情