刚刚跨了年,我在朋友圈里看到欢天喜地的景象。这个时刻我突然间想起前两天下班后,我在家翻着红宝书听着歌,翻到了高级程序设计(第4版)的第234页,那一页是这样的
我看到这里就开始困了,可是看着看着就不困了,我把代码简化一下
Person.prototype = {
name:'zhaoxi'
}
}
let friend = new Person()
console.log(friend instanceof Person); //1 true(错误)
console.log(friend instanceof Object ); //2 true
console.log(friend.constructor ==Person) //3 false (错误)
console.log(friend.constructor ==Object) //4 true (错误)
平平无奇的代码,红宝书上说通过
Person.prototype = { name:'zhaoxi' }
这种方式给原型添加属性,会导致 Person.prototype
的原型发生变化
所以上面的1,2,3,4,分别打印为 true true false true
看到这里,小张的眉头皱了起来,这种方式原型会发生变化没错,一般情况化,我们会这样添加属性
Person.prototype.name = 'zhaoxi'
但是你要是理所当然的认为
Person.prototype = { name:'zhaoxi' }
和刚才是一样的,就会有很大的隐患
所有的对象都是函数创建的
你可能会说这样 let obj = {a:1}
就不是
其实这种通过字面量创建对象的写法只是一个语法糖,
本质上,他等于let obj = new Object({a:1})
我们验证一下
let obj = {a:1}
console.log(obj.__proto__==Object.prototype) //true
console.log(obj instanceof Object) //true
数组也是一样
let arr = [1,2,3,4]
console.log(arr.__proto__==Array.prototype)
console.log(arr instanceof Array)
这里先不展开说,当看到这里,你可能就会明白,刚刚的两种给原型赋值的方式为何是不同的了
是的
Person.prototype = { name:'zhaoxi' }
这种方法,相当于Person的原型设置成为了新的对象,他和之前的Person.prototype已经不是一个对象创建出来的了
那红宝书上有什么问题呢
constructor(构造器)等于函数本身,指向创建他的那个函数
说这个问题之前,我们介绍一下constructor这个小老弟
每个函数都默认有一个propotype属性,是一个对象。这个对象默认只有一个属性constructor
知道了这一点在回到刚开始
let friend = new Person()
console.log(friend.constructor ==Person) //3 false (错误) 应当为true
console.log(friend.constructor ==Object) //4 true (错误) //应当为false
就知道刚刚红宝书的判断是不正确的了
就这就能证明?当然不
为什么要弄这个乱七八糟的原型呢? 因为原型很重要,js的继承是基于原型实现的
每个对象都有一个隐藏的属性——“proto”,这个属性引用了创建这个对象的函数的prototype。即:实例.proto === 原型.prototype
我们刚刚提到,每个对象默认只有一个属性,那为何我们新建的对象 除了构造器还有这么多其他的属性呢 我们可以看到这个对象的构造器(constructor)是指向Object的,因为他是有Object创建的 这些属性也是Object对象上的,之所以我们能看到并且可以使用 在这里就是因为obj.proto === Object.prototype
这就是原型链,当对象在寻找一个属性的时候,首先会在自己本身找,找不到就会去他的原型上找,直到null 就像这个,不属于obj实例的属性,只要原型上有,就可以使用,这也会产生for in之类循环不是自己想要的结果之类的副作用,以至于出现hasOwnProperty,in,Object.keys等解决方法,不过这暂时和本篇文章无关
讲这些是因为和instanceof有关
instanceof
instanceof运算符的第一个变量是一个对象,暂时称为A;第二个变量一般是一个函数,暂时称为B。
instanceof的判断队则是:沿着A的__proto__这条线来找,同时沿着B的prototype这条线来找,如果两条线能找到同一个引用,即同一个对象,那么就返回true。如果找到终点还未重合,则返回false。
就像刚刚举的那个例子
这个地方容易记错,写这篇文章之前,我问了一位未来的大佬红宝书错了吗,他说起constructor 他就理解错了,constructor指向的是创建他的那个函数,而instanceof是通过原型链来判断的
理解了这个,结合刚刚说的,我们可以解释很多看起来很奇怪的现象
一句话,js中,所有的对象都是函数创建的,所有的引用类型都是对象 ,因为他们可以从原型链中找到彼此,说起来倒有点鸡生蛋,蛋生鸡的味道
那么,回到红宝书的剩下的两个对比
let a = Person.prototype.constructor //第一个构造函数是自己
Person.prototype = {
name:'zhaoxi'
}
}
let friend = new Person()
console.log(friend instanceof Person); //1 true(错误)
console.log(friend instanceof Object ); //2 true
console.log(friend instanceof Person)
本质上,就等于
console.log(friend.__proto__==Person.prototype)
,这对吗?
这不对
还记得刚开始我们提到的吗,使用这种字面量赋值,是一种语法糖,本质上,这里的
Person.prototype = { name:'zhaoxi' }
等于Person.prototype =new Object({ name:'zhaoxi' })
问题就出在这里,这里的Person.prototype
是由Object
创建的,也就是说
friend.__proto__==Object.prototype
这才是对的
friend的隐式原型指向的不是Person.prototype,而是Person.prototype={}产生的新对象
因为新对象是由Person.prototype new Object出来的,所以
console.log(friend.__proto__.__proto__==Object.prototype) //新对象的构造器指向Object //true
console.log(friend.__proto__.__proto__==Person.prototype) //而不是指向Person //false
所以,应该是这样
Person.prototype = {
name:'zhaoxi'
}
}
let friend = new Person()
console.log(friend instanceof Person); //1 false
console.log(friend instanceof Object ); //2 true
console.log(friend.constructor ==Person) //3 true
console.log(friend.constructor ==Object) //4 false
读完红宝书不是目的,读红宝书才是目的。这篇文章论证或许还有问题,希望大家多多指正
新的2021年到了,虽然本质上看,年份只是人们用来计算时间的一种称谓,每一天都在均匀流淌没有特别之处,但大家都在期翼着新的一年会带来新的生活,或许生活总该期盼些什么才好吧
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!