系列文章推荐
JavaScript原型与原型链(基础篇)
JavaScript原型与原型链(进阶篇)
JavaScript原型与原型链(总结篇)
1 intanceof运算符
instanceof运算符用于判断构造函数的prototype属性是否出现在对象的原型链中,使用方法如下:
function Cat(name, age) {
this.name = name
this.age = age
}
const kat = new Cat('kat', 2)
console.log(kat instanceof Cat) // true
console.log(kat instanceof Object) // true
在这个例子中,由于kat.__proto__ === Cat.prototype,所以kat instanceof Cat输出为true;由于原型链的终点为Object.prototype,所以Object.prototype肯定在kat的原型链上,所以kat instanceof Object输出也为true。
下面是自定义instanceof的实现代码,实现思路为沿着原型链一个接一个的判断,当instance.__proto__ === constructor.prototype时返回true,否则返回false:
function myInstanceof(instance, constructor) {
// 获取实例对象的原型
let proto = Object.getPrototypeOf(instance)
// 获取构造函数的prototype对象
let prototype = constructor.prototype;
// 判断构造函数的prototype属性是否在实例对象的原型链上
while (true) {
// 若!proto === null则说明已经到原型的终点Object.prototype
if (!proto) {
return false
}
// 实例对象的原型与构造函数的原型相等
if (proto === prototype) {
return true
}
// 如果没有找到,就继续在原型上找
proto = Object.getPrototypeOf(proto)
}
}
2 函数的原型
2.1 函数也是一种对象
在JavaScript中函数也是一种对象类型,如下面代码中创建一个add函数:
function add(x, y) {
return x + y
}
console.log(add instanceof Object) // true
等价于使用new操作符和Function构造函数创建一个函数对象add:
let add = new Function('x', 'y', 'return x + y')
console.log(add instanceof Object) // true
上面两种写法是等价的,举这个例子是为了说明一个问题,即函数也是一个对象;同样的,构造函数也是函数,所以构造函数也是一个对象,既然是对象,那么就存在原型,所有构造函数的原型对象都是Object构造函数的实例对象,于是满足关系:
function Cat() {}
const kat = new Cat()
console.log(kat.__proto__.__proto__ === Object.prototype) // true
// kat.__proto__ === Cat.prototype
console.log(Cat.prototype.__proto__ === Object.prototype) // true
上述关系可以用如下图例表示:

2.2 函数的构造函数Function
在JavaScript中的所有函数都是由Function构造函数实例化而来,因此存在如下关系:
function Cat() {}
console.log(Cat.__proto__ === Function.prototype) // true
console.log(Cat.__proto__ === Cat.constructor.prototype) // true
上述关系可以用如下图例表示:

3 “奇怪”的Object和Function
3.1 引例
下面例子用于和后文中的例子作为对照,例中声明构造函数Cat,并实例化对象kat,并且所有的结论在前文中已经提到:
function Cat(name, age) {
this.name = name
this.age = age
}
const kat = new Cat('kat', 2)
console.log(kat.__proto__ === Cat.prototype) // true
console.log(Cat.__proto__ === Function.prototype) // true
console.log(Cat.prototype.__proto__ === Object.prototype) // true
console.log(kat instanceof Cat) // true
console.log(kat instanceof Object) // true
console.log(Cat instanceof Cat) // false
console.log(Cat instanceof Object) // true
3.2 Object和Function
这里直接给出结论,下面四个操作的运算结果都为true:
Function instanceof Object
Object instanceof Function
Function instanceof Function
Object instanceof Object
已知instanceof运算符的原理是判断构造函数的prototype属性是否出现在对象的原型链上,根据上述结论还能推导及验证出如下关系:
- 由
Function instanceof Object,推导出Function.__proto__.__proto__ === Object.prototype; - 由
Object instanceof Function,推导出Object.__proto__ === Function.prototype; - 由
Function instanceof Function,推导出Function.__proto__ === Function.prototype; - 由
Object instanceof Object,推导出Object.__proto__.__proto__ === Object.prototype。
由上述第三个关系可知:Function自身是构造函数,且自身又是自身构造函数的实例,这种关系是非常“奇怪”的,和“先有鸡还是先有蛋?”这个问题一样,在3.1节的例子中对于构造函数Cat是不存在这种关系的,这种关系只存在于Function上。
3.3 既是函数,也是对象
为了解释3.2节中的问题,这里把3.2节中推导出的四个关系分为两组:
- 对象组:
Function.__proto__.__proto__ === Object.prototype;Object.__proto__.__proto__ === Object.prototype。
- 函数组:
Object.__proto__ === Function.prototype;Function.__proto__ === Function.prototype。
之所以这么分组,是因为看待事物的角度不同,会导致结果的不同。
3.3.1 对象组
先看下面例子:
function Cat(name, age) {
this.name = name
this.age = age
}
const kat = new Cat('kat', 2)
console.log(kat.__proto__.__proto__ === Object.prototype) // true
// Cat.__proto__ === Function.prototype
// Function.prototype.__proto__ === Object.prototype
console.log(Cat.__proto__.__proto__ === Object.prototype) // true
在这个例子中声明构造函数Cat,并实例化对象kat,前面提到构造函数Cat也是一种对象,所以满足关系:
kat.__proto__.__proto__ === Object.prototype;Cat.__proto__.__proto__ === Object.prototype。
上述关系可以用如下图例表示:

把这个结论推广到Function和Object上,由于可以把Function和Object看作一种对象,所以满足关系:
Function.__proto__.__proto__ === Object.prototype;Object.__proto__.__proto__ === Object.prototype。
3.3.2 函数组
先看下面例子:
function Cat() {}
console.log(Cat.__proto__ === Function.prototype) // true
在JavaScript中的所有函数都是由Function构造函数实例化而来,在这个例子中构造函数Cat是由Function构造函数实例化而来,具体见2.2节,因此满足如下关系:
Cat.__proto__ === Function.prototype。
把这个结论推广到Function和Object上,由于可以把Function和Object看作一种函数,所以满足关系:
Object.__proto__ === Function.prototype;Function.__proto__ === Function.prototype。
3.4 function anonymous()
其实在3.3.1节中还有一个疑点,即:
kat.__proto__.__proto__ === Object.prototype // true
Cat.__proto__.__proto__ === Object.prototype // true
此时读者可能觉得kat.__proto__ === Cat.__proto__也是成立的,其实不然,这个问题的落脚点在于kat.__proto__和Cat.__proto__到底是什么,见下面例子:
function Cat(name, age) {
this.name = name
this.age = age
}
const kat = new Cat('kat', 2)
console.dir(kat.__proto__)
console.dir(Cat.__proto__)
输出的结果如下:

对于kat.__proto__,根据前文中的结论,可以判断出kat.__proto__ === Cat.prototype,输出结果也是如此。
对于Cat.__proto__输出的是之前没有见过的函数function anonymous(),译为匿名函数,由于构造函数Cat是由构造函数Function实例化而来,所以Cat.__proto__ === Function.prototype,事实上所有构造函数的__proto__值都为function anonymous(),又由于Function.prototype === Function.__proto__,所以Function.prototype和Function.__proto__的值也为函数function anonymous():
- 当把
Function看作函数时,Function.prototype === function anonymous(),即所有由Function构造函数实例化的其他构造函数的原型都为function anonymous(); - 当把
Function看作对象时,Function.__proto__ === function anonymous(),由于Function自身是构造函数,且自身又是自身构造函数的实例,所以实例对象Function的原型为function anonymous()。
上述关系可以用如下图例表示:

根据结论“所有构造函数的原型对象都是Object构造函数的实例对象”,所以Cat.__proto__.__proto__ === Object.prototype。
4 其他内置构造函数
JavaScript中的内置构造函数参考:MDN - Global_Objects,其他内置构造函数指的是除去Function和Object之外的构造函数,这里仅列举常见的几种,如Number、String和Array,具有如下特点:
- 和
Function和Object一样,其他内置构造函数的__proto__属性也等于Function.prototype:
console.log(Number.__proto__ === Function.prototype) // true
console.log(String.__proto__ === Function.prototype) // true
console.log(Array.__proto__ === Function.prototype) // true
- 其他内置构造函数不满足
instanceof运算符自身与自身计算为true的特点:
console.log(Number instanceof Number) // false
console.log(String instanceof String) // false
console.log(Array instanceof Array) // false
注意区分内置构造函数和内置对象,JavaScript中的内置对象,如
Math和JSON是以对象形式存在的,满足如下关系:
Math.__proto__ === Object.prototype;JSON.__proto__ === Object.prototype。

&spm=1001.2101.3001.5002&articleId=123968380&d=1&t=3&u=6673a2027cc2492d84b957e2669e6d12)
184

被折叠的 条评论
为什么被折叠?



