一、[[Prototype]]

JavaScript中的对象有一个特殊的[[Prototype]]内置属性,其实就是对于其他对象的引用。

var myObject = {
a: 2
};
myObject.a; //

当你试图引用对象的属性时会触发原型[[Get]]操作,比如myObject.a。

1. 第一步是检查对象本身是否有这个属性,如果有的话就使用它。

2. 如果a不在myObject中,就需要使用对象的[[Prototype]]链了。

1)Object.prototype

普通的[[Prototype]]链最终都会指向内置的Object.prototype。

2)属性设置和屏蔽

myObject.foo = "bar";

如 果 属 性 名foo既 出 现 在myObject中 也 出 现 在myObject的[[Prototype]]链 上 层, 那么就会发生屏蔽

myObject中包含的foo属性会屏蔽原型链上层的所有foo属性,因为myObject.foo总是会选择原型链中最底层的foo属性。

有些情况下会隐式产生屏蔽:

var anotherObject = {
a: 2
}; var myObject = Object.create(anotherObject); anotherObject.a; //
myObject.a; // 2
anotherObject.hasOwnProperty("a"); // true
myObject.hasOwnProperty("a"); // false myObject.a++; // 隐式屏蔽! anotherObject.a; // 2
myObject.a; // myObject.hasOwnProperty("a"); // true

++操作相当于myObject.a = myObject.a + 1。

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

二、“类”

JavaScript中只有对象

在JavaScript中,类无法描述对象的行,(因为根本就不存在类!)对象直接定义自己的行为。

1)“类”函数

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

a这个对象是在调用new Foo()时创建的,最后会被关联到这个“Foo.prototype”对象上。

在JavaScript中不能创建一个类的多个实例,只能创建多个对象,它们[[Prototype]]关联的是同一个对象。

从视觉角度来说,[[Prototype]]机制如下图所示,箭头从右到左,从下到上:

这个机制通常被称为原型继承,它常常被视为动态语言版本的类继承。

2)“构造函数

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

之所以认为Foo是一个“类”:

1. 其中一个原因是我们看到了关键字new,在面向类的语言中构造类实例时也会用到它。

2. 另一个原因是,看起来我们执行了类的构造函数方法,Foo()的调用方式很像初始化类时类构造函数的调用方式

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

函数不是构造函数,但是当且仅当使用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"

原型继承的机制,是指a可以“继承”Foo.prototype并访问Foo.prototype的myName()函数。

面这两种方式是常见的错误做法

// 和你想要的机制不一样!
Bar.prototype = Foo.prototype; // 基本上满足你的需求,但是可能会产生一些副作用 :(
Bar.prototype = new Foo();

1. 第一种只是让Bar.prototype直接引用Foo.prototype对象。因此当你执行类似Bar.prototype.myLabel = ...的赋值语句时会直接修改Foo.prototype对象本身

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

两种正确的把Bar.prototype关联到Foo.prototype的方法:

// ES6 之前需要抛弃默认的 Bar.prototype
Bar.ptototype = Object.create( Foo.prototype ); // ES6 开始可以直接修改现有的 Bar.prototype
Object.setPrototypeOf( Bar.prototype, Foo.prototype );

1)检查“类”关系

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

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

如何通过内省找出a的“祖先”(委托关联)呢?

1. 第一种站在“类”的角度来判断:

a instanceof Foo; // true

instanceof回答的问题是:在a的整条[[Prototype]]链中是否有指向Foo.prototype的对象?

这个方法只能处理对象(a)和函数(带.prototype引用的Foo)之间的关系。

2. 第二种判断[[Prototype]]反射的方法,它更加简洁:

Foo.prototype.isPrototypeOf( a ); // true

isPrototypeOf回答的问题是:在a的整条[[Prototype]]链中是否出现过Foo.prototype?

同样的问题,同样的答案,但是在第二种方法中并不需要间接引用函数(Foo),它的.prototype属性会被自动访问。

我们只需要两个对象就可以判断它们之间的关系。举例来说:

// 非常简单:b 是否出现在 c 的 [[Prototype]] 链中?
b.isPrototypeOf( c );

2)获取一个对象的[[Prototype]]链

1. 在ES5中,标准的方法是:

Object.getPrototypeOf( a );

console.log(Object.getPrototypeOf( a ) === Foo.prototype); // true

2. 浏览器也支持一种非标准的方法来访问内部[[Prototype]]属性:

a.__proto__ === Foo.prototype; // true

.__proto__的实现大致上是这样的:

Object.defineProperty(Object.prototype, "__proto__", {
get: function() {
return Object.getPrototypeOf(this);
},
set: function(o) {
// ES6 中的 setPrototypeOf(..)
Object.setPrototypeOf(this, o);
return o;
}
});

访问(获取值)a.__proto__时,实际上是调用了a.__proto__()(调用getter函数)。

虽然getter函数存在于Object.prototype对象中,但是它的this指向对象a,所以和Object.getPrototypeOf( a )结果相同

四、对象关联

[[Prototype]]机制就是存在于对象中的一个内部链接,它会引用其他对象

这个链接的作用是:如果在对象上没有找到需要的属性或者方法引用,引擎就会继续在[[Prototype]]关联的对象上进行查找。

这一系列对象的链接被称为“原型链”。

var foo = {
something: function() {
console.log("Tell me something good...");
}
};
var bar = Object.create(foo);
bar.something(); // Tell me something good...

我们并不需要类来创建两个对象之间的关系,只需要通过委托来关联对象就足够了。

Object.create()的polyfill代码:

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

使用了一个一次性函数F,我们通过改写它的.prototype属性使其指向想要关联的对象,然后再使用new F()来构造一个新对象进行关联。

《你不知道的JavaScript》整理(四)——原型的更多相关文章

  1. 你不知道的JavaScript(四)数值

    JS中只有一种数值类型,即number.不管是整数还是小数都属于number类型,事实上JS并不区分小数和整数. <div> <script type="text/java ...

  2. 《你不知道的JavaScript》整理(二)——this

    最近在读一本进阶的JavaScript的书<你不知道的JavaScript(上卷)>,这次研究了一下“this”. 当一个函数被调用时,会创建一个活动记录(执行上下文). 这个记录会包含函 ...

  3. 《你不知道的JavaScript》整理(一)——作用域、提升与闭包

    最近在读一本进阶的JavaScript的书<你不知道的JavaScript(上卷)>,里面分析了很多基础性的概念. 可以更全面深入的理解JavaScript深层面的知识点. 一.函数作用域 ...

  4. 读书笔记-你不知道的JavaScript(上)

    本文首发在我的个人博客:http://muyunyun.cn/ <你不知道的JavaScript>系列丛书给出了很多颠覆以往对JavaScript认知的点, 读完上卷,受益匪浅,于是对其精 ...

  5. JavaScript中的this(你不知道的JavaScript)

    JavaScript中的this,刚接触JavaScript时大家都在大肆渲染说其多么多么的灵巧重要,然而自己并不关心:随着自己对JavaScript一步步深入了解,突然恍然大悟,原来它真的很重要!所 ...

  6. 读《你不知道的JavaScript(上卷)》后感-浅谈JavaScript作用域(一)

    原文 一. 序言 最近我在读一本书:<你不知道的JavaScript>,这书分为上中卷,内容非常丰富,认真细读,能学到非常多JavaScript的知识点,希望广大的前端同胞们,也入手看看这 ...

  7. 《你不知道的 JavaScript 上卷》 学习笔记

    第一部分: 作用域和闭包 一.作用域 1. 作用域:存储变量并且查找变量的规则 2. 源代码在执行之前(编译)会经历三个步骤: 分词/此法分析:将代码字符串分解成有意义的代码块(词法单元) 解析/语法 ...

  8. 《你不知道的JavaScript》系列分享专栏

    <你不知道的JavaScript>系列分享专栏 你不知道的JavaScript”系列就是要让不求甚解的JavaScript开发者迎难而上,深入语言内部,弄清楚JavaScript每一个零部 ...

  9. JavaScript词法作用域—你不知道的JavaScript上卷读书笔记(一)

    前段时间在每天往返的地铁上抽空将 <你不知道的JavaScript(上卷)>读了一遍,这本书很多部分写的很是精妙,对于接触前端时间不太久的人来说,就好像是叩开了JavaScript的另一扇 ...

  10. 【前端性能优化】高性能JavaScript整理总结

    高性能JavaScript整理总结 关于前端性能优化:首先想到的是雅虎军规34条然后最近看了<高性能JavaScript>大概的把书中提到大部分知识梳理了下并加上部分个人理解这本书有参考雅 ...

随机推荐

  1. UWP中新加的数据绑定方式x:Bind分析总结

    UWP中新加的数据绑定方式x:Bind分析总结 0x00 UWP中的x:Bind 由之前有过WPF开发经验,所以在学习UWP的时候直接省略了XAML.数据绑定等几个看着十分眼熟的主题.学习过程中倒是也 ...

  2. 匹夫细说C#:庖丁解牛迭代器,那些藏在幕后的秘密

    0x00 前言 在匹夫的上一篇文章<匹夫细说C#:不是“栈类型”的值类型,从生命周期聊存储位置>的最后,匹夫以总结和后记的方式涉及到一部分迭代器的知识.但是觉得还是不够过瘾,很多需要说清楚 ...

  3. scrapy 知乎用户信息爬虫

    zhihu_spider 此项目的功能是爬取知乎用户信息以及人际拓扑关系,爬虫框架使用scrapy,数据存储使用mongo,下载这些数据感觉也没什么用,就当为大家学习scrapy提供一个例子吧.代码地 ...

  4. MVC Core 网站开发(Ninesky) 2.1、栏目的前台显示

    上次创建了栏目模型,这次主要做栏目的前台显示.涉及到数据存储层.业务逻辑层和Web层.用到了迁移,更新数据库和注入的一些内容. 一.添加数据存储层 1.添加Ninesky.DataLibrary(与上 ...

  5. 前端学HTTP之实体和编码

    前面的话 每天都有各种媒体对象经由HTTP传送,如图像.文本.影片以及软件程序等.HTTP要确保它的报文被正确传送,识别.提取以及适当处理.为了实现这些目标,HTTP使用了完善的标签来描述承载内容的实 ...

  6. 引人瞩目的 CSS 变量(CSS Variable)

    这是一个令人激动的革新. CSS 变量,顾名思义,也就是由网页的作者或用户定义的实体,用来指定文档中的特定变量. 更准确的说法,应该称之为 CSS 自定义属性 ,不过下文为了好理解都称之为 CSS 变 ...

  7. Python 正则表达式入门(中级篇)

    Python 正则表达式入门(中级篇) 初级篇链接:http://www.cnblogs.com/chuxiuhong/p/5885073.html 上一篇我们说在这一篇里,我们会介绍子表达式,向前向 ...

  8. 似懂非懂的localStorage和sessionStorage

    一.区别 相信很多人都见过这两个关于HTML5的新名词!HTML5种的web storage包含两种存储方式:localStorage和sessionStorage,这两种方式存储的数据不会自动发给服 ...

  9. Win10提示没有权限使用网络资源问题解决

    借鉴链接:http://www.cr173.com/html/67361_1.html Win10提示没有权限使用网络资源解决方法 1.打开控制面板; 2.在所有控制面板项中找到凭据管理器; 3.添加 ...

  10. XAMARIN.ANDROID SIGNALR 实时消息接收发送示例

    SignalR 是一个开发实时 Web 应用的 .NET 类库,使用 SignalR 可以很容易的构建基于 ASP.NET 的实时 Web 应用.SignalR 支持多种服务器和客户端,可以 Host ...