原型
相信js开发者都知道原型,原型链,但是很多人晕晕乎乎对此不知甚解。下面分享一下我的个人心得。
学习中的困惑
- 构造函数,原型,实例对象之间的关系是什么?
- 原型链是怎么继承的?
- 既然构造函数本身是函数,那么new和直接调用有什么区别,
解答
构造函数,原型,实例对象之间的关系是什么?
废话不说先上图
不需要管网上乱七八糟的各种原型链指向图,记住这个就行了。
除了各种指向以外,图中还包含这些信息:
- 'prototype'属性是函数独有的
-
__proto__
才是实例的。当然,这个属性不建议使用。可以用Object.setPrototypeOf
和Object.getPrototypeOf
天真的我曾经以为原型是prototype
,那原型链就是跟着prototype
找...,实在是too young too simple啊。
这里一定要记住,原型链是沿着__proto__
链找。
原型链是怎么继承的?
其实这个问题也可以是:当我们构建对象时,发生了什么。
举个简单例子:
function father(){
this.name = "爸爸"
}
father.prototype.family = "爱的家"
var dad = new father();
这里进行了如下操作:
- 实例化一个空对象
Object.create()
; - 将father的[[prototype]]属性委托给father的原型。
- 指定上下文。即this为第一步实例化出来的空对象。
- 执行构造函数。
用代码模拟就是:
function _new(/* 构造函数 */ constructor, /* 构造函数参数 */ param1) {
// 将 arguments 对象转为数组
var args = [].slice.call(arguments);
// 取出构造函数
var constructor = args.shift();
// 创建一个空对象,继承构造函数的 prototype 属性
var context = Object.create(constructor.prototype);
// 执行构造函数
var result = constructor.apply(context, args);
// 如果返回结果是对象,就直接返回,则返回 context 对象
return (typeof result === 'object' && result != null) ? result : context;
}
// 实例
var actor = _new(Person, '张三', 28);
记住一点,实例化对象以后,构造函数就没什么事了。原型链只看构造函数的原型对象和实例化出来的对象。
这里在说一点,关于,继承。现实中的继承就是你的东西给我了,我拿过来了。我可以使用,可以转让。但是js中真是这样吗?
一个很简单的问题,如果原型继承真是把原型中的属性拷过来那我每次自身找不到属性还要沿着原型链查找干嘛。
很显然,js中原型可不是二百五,他所谓的继承,只是给你使用权,不会给你转让权。
上面第二步中:将father的[[prototype]]属性委托给father的原型
。我写的是委托。这样描述可能更准确。因为这里只是相当于给实例化对象一个指针,指向了原型对象。这其实也是下一个问题的答案。
既然构造函数本身是函数,那么new和直接调用有什么区别
答案揭晓:
只有通过new调用构造函数才会走第二步,也就是原型的委托操作。
那么怎么判断是否同new调用呢?
判断是否通多new调用函数
ES6函数内部可以使用new.target属性。如果当前函数是new命令调用,new.target指向当前函数,否则为undefined。
ES5
if(this instanceof Father){...}
ES6
//在函数外使用new.target是一个错误
typeof new.target !== 'undefined'
这里再多扯一下typeof和instance。反正我以前是每次看了记住了,很久不用又忘了。。。。,看了这么多原型链的内容,再看看这个应该会映象深刻吧:
typeof :
用来判断值类型。如string/number/boolean,但如果判断引用类型,返回值就只有 object/function。所以无法进一步判断是object对象,还是数组,还是new Number等等。
注意:
undefined返回undefined。
// 错误的写法
if (v) {
// ...
}
// ReferenceError: v is not defined
// 正确的写法
if (typeof v === "undefined") {
// ...
}
instanceof :
根据原型链判断引用类型。A instanceof B
,判断原则是沿着 A 的__proto__这条线来找,同时沿着 B 的 prototype 这条线来找,如果两条线能找到同一个引用,即同一个对象,那么就返回 true。如果找到终点还未重合,则返回 false。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。