We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
不知道你有没有想过这样一些问题:
为什么我定义一个数组,它就有 push、join、pop、shift 等方法,我明明什么也没写啊? 为什么我定义一个函数,它就有 call、apply、length 等属性/方法,我也什么都没有做呀?! 为什么我定义一个对象,它就有 toString、valueOf 等方法,我更是什么都没有做呀?
当我们定义一个对象时
var obj = {}
我们发现 obj 下面有一个属性名叫__proto__,(它是个对象)
__proto__
obj.__proto__里面又有很多属性,包括 valueOf、toString、constructor 等。当我们需要找obj.valueOf这个属性时,发现 obj 本身没有没有,那么它就会去查找obj.__proto__是否有这个属性,如果还没有,它去找obj.__proto__.__proto__,直到找到这个属性或 null 为止,在这个读取属性的过程中,是沿着__proto__组成的链子来搜索的,这个链子我们称为原型链。
obj.__proto__
obj.valueOf
obj.__proto__.__proto__
如果 obj 自身定义了一个 valueOf 属性,那么它找到自身的 valueOf 之后就不再沿着__proto__来找,因为已经找到了,没有必要继续找了,也就是说
新增的属性不会沿着__proto__查找
读取属性会沿着__proto__,直到找到这个属性,或者是 null 为止。
__proto__是一个简单的访问器属性,它总是指向它的构造函数的 prototype ,即原型对象。
所有的对象都继承了Object.prototype的属性和方法, 它们可以被覆盖(除了以null为原型的对象,如 Object.create(null))。 例如,新的构造函数的原型覆盖原来的构造函数的原型,提供它们自己的 toString() 方法.。对象的原型的改变会传播到所有对象上,除非这些属性和方法被其他对原型链更里层的改动所覆盖。
所有的对象会动态生成一个__proto__指向它构造函数的原型 ( prototype )
当我们去查找obj.valueOf这个属性时,他会沿着原型链去查找obj.__proto__.valueOf,而obj.__proto__指向obj.constructor.prototype。即
obj.__proto__.valueOf
obj.constructor.prototype
obj.__proto__ === obj.constructor.prototype // true obj.__proto__.toString=== obj.constructor.prototype.toString // true
我们知道 obj 的构造函数就是 Object,那么我们也可以这么写
obj.__proto__ === Object.prototype // true obj.__proto__.toString === Object.prototype.toString // true
以 push 方法为例,我们知道当我们定义一个空数组时,我们可以直接调用 push 方法,根据上面的解释,他会沿着这个数组的__proto__去查找这个方法。
var array = [] array.push === array.__proto__.push // true
array.__proto__指向它构造函数的prototype,那么
array.__proto__
array.__proto__.push === array.constructor.prototype.push // true array.__proto__.push === Array.prototype.push // true
终于找到 push 方法了。
问题来了,我们知道 array 也是对象,那么 JS 是怎么知道 array 也是对象的呢?
我们先看Array.__proto__指向谁
Array.__proto__
Array.__proto__ === Function.prototype // true
你也许会纳闷,怎么Array.__proto__指向的怎么是函数的原型对象呢?因为 Array 的构造函数就是函数,可以通过console.log(Array.constructor)验证。而函数的原型对象的__proto__最终指向 Object
console.log(Array.constructor)
Function.prototype.__proto__ === Object.prototype // true Array.__proto__.__proto__ === Object.prototype // true
或者我们也可以这样写
Array.prototype.__proto__ === Object.prototype // true
最终归宿都是 Object。
至此我们看下一个小小的 array 都经历了什么
array.__proto__ ----> Array.prototype ----> Array.prototype.__proto__---->Object.prototype
这样也就不难理解为什么数组(函数)也是个对象了。
也许你会迷惑,既然Array.__proto__.__proto__ = Object.prototype,那么
Array.__proto__.__proto__ = Object.prototype
Number.__proto__.__proto__ === Object.prototype // true Boolean.__proto__ .__proto__ === Object.prototype // true String.__proto__.__proto__ === Object.prototype // true
为什么我们不可以说数字/布尔/字符串也是对象呢?
这要看这个数字/布尔/字符串是怎么创建的了。
以数字为例
var a = 1 var b = new Number(1)
我们知道基本类型是没有属性的,即便可以访问到这个属性,也是访问的临时对象的属性,访问完就销毁了,即使你发现a.__proto__.__proto__指向是 Object,也是 new Number 指向的,跟 a 没有半毛钱关系,因为 a 就是个 number。
a.__proto__.__proto__
b 就不一样了,b 是构造函数 Number 构造出来的一个对象,只不过他的值是 1,它可是有__proto__属性的,那么 b 就可以愉快的指来指去了。
b.__proto__.__proto__ === Object.prototype // true
所以 a 是一个 number,而 b 是一个 object。
同理字符串和布尔也是如此。
最后,我们来一起念一遍 JS 的七种数据类型:
number , string , boolean , undefined , null , object , symbol
那么 JS 一切皆对象 的说法不攻自破了。
什么是 JS 原型链?
Object.prototype.proto
The text was updated successfully, but these errors were encountered:
No branches or pull requests
不知道你有没有想过这样一些问题:
为什么我定义一个数组,它就有 push、join、pop、shift 等方法,我明明什么也没写啊?
为什么我定义一个函数,它就有 call、apply、length 等属性/方法,我也什么都没有做呀?!
为什么我定义一个对象,它就有 toString、valueOf 等方法,我更是什么都没有做呀?
我们先来说说对象
当我们定义一个对象时
我们发现 obj 下面有一个属性名叫
__proto__
,(它是个对象)obj.__proto__
里面又有很多属性,包括 valueOf、toString、constructor 等。当我们需要找obj.valueOf
这个属性时,发现 obj 本身没有没有,那么它就会去查找obj.__proto__
是否有这个属性,如果还没有,它去找obj.__proto__.__proto__
,直到找到这个属性或 null 为止,在这个读取属性的过程中,是沿着__proto__
组成的链子来搜索的,这个链子我们称为原型链。如果 obj 自身定义了一个 valueOf 属性,那么它找到自身的 valueOf 之后就不再沿着
__proto__
来找,因为已经找到了,没有必要继续找了,也就是说新增的属性不会沿着
__proto__
查找读取属性会沿着
__proto__
,直到找到这个属性,或者是 null 为止。那么obj.__proto__到底是什么呢?
__proto__
是一个简单的访问器属性,它总是指向它的构造函数的 prototype ,即原型对象。所有的对象会动态生成一个
__proto__
指向它构造函数的原型 ( prototype )当我们去查找
obj.valueOf
这个属性时,他会沿着原型链去查找obj.__proto__.valueOf
,而obj.__proto__
指向obj.constructor.prototype
。即我们知道 obj 的构造函数就是 Object,那么我们也可以这么写
对于数组
以 push 方法为例,我们知道当我们定义一个空数组时,我们可以直接调用 push 方法,根据上面的解释,他会沿着这个数组的
__proto__
去查找这个方法。array.__proto__
指向它构造函数的prototype,那么终于找到 push 方法了。
问题来了,我们知道 array 也是对象,那么 JS 是怎么知道 array 也是对象的呢?
__proto__
我们先看
Array.__proto__
指向谁你也许会纳闷,怎么
Array.__proto__
指向的怎么是函数的原型对象呢?因为 Array 的构造函数就是函数,可以通过console.log(Array.constructor)
验证。而函数的原型对象的__proto__
最终指向 Object或者我们也可以这样写
最终归宿都是 Object。
至此我们看下一个小小的 array 都经历了什么
这样也就不难理解为什么数组(函数)也是个对象了。
一切皆对象?
也许你会迷惑,既然
Array.__proto__.__proto__ = Object.prototype
,那么为什么我们不可以说数字/布尔/字符串也是对象呢?
这要看这个数字/布尔/字符串是怎么创建的了。
以数字为例
我们知道基本类型是没有属性的,即便可以访问到这个属性,也是访问的临时对象的属性,访问完就销毁了,即使你发现
a.__proto__.__proto__
指向是 Object,也是 new Number 指向的,跟 a 没有半毛钱关系,因为 a 就是个 number。b 就不一样了,b 是构造函数 Number 构造出来的一个对象,只不过他的值是 1,它可是有
__proto__
属性的,那么 b 就可以愉快的指来指去了。所以 a 是一个 number,而 b 是一个 object。
同理字符串和布尔也是如此。
最后,我们来一起念一遍 JS 的七种数据类型:
number , string , boolean , undefined , null , object , symbol
那么 JS 一切皆对象 的说法不攻自破了。
推荐阅读
什么是 JS 原型链?
Object.prototype.proto
The text was updated successfully, but these errors were encountered: