【刷题笔记】javascript里面的instance of

最近刷到一道JavaScript的题目

  var tmp = {};
  var A = function() {};
  A.prototype = tmp;

  var a = new A();
  A.prototype = {};

  var b = Object.create(tmp);
  b.constructor = A.constructor;

  console.log(a instanceof A);
  console.log(b instanceof A);
  

在公布答案之前,先来看看JavaScript里面的instance of 是个啥玩意

根据定义 instanceof 运算符用来检测 constructor.prototype 是否存在于参数 object 的原型链上。
他可以用在如下几个方面:

  • 1.instanceof的普通的用法,obj instanceof Object 检测Object.prototype是否存在于参数obj的原型链上。
  • 2.继承中判断实例是否属于它的父类

但是有时候会遇到像下面这样的代码判断:

console.log(Object instanceof Object);//true 
console.log(Function instanceof Function);//true 
console.log(Number instanceof Number);//false 
console.log(String instanceof String);//false  
console.log(Function instanceof Object);//true  
console.log(Foo instanceof Function);//true 
console.log(Foo instanceof Foo);//false

这可咋整啊

先来看看instanceof运算符的判断逻辑:

function instance_of(L, R) {//L 表示左表达式,R 表示右表达式 
  var O = R.prototype;// 取 R 的显示原型 
  L = L.__proto__;// 取 L 的隐式原型 
  	while (true) {   
  		 if (L === null)      
  			return false;    
  		if (O === L)// 当 O 严格等于 L 时,返回 true      
  			return true;   
  		 L = L.__proto__; 
  			 } 
}

好了,这里又涉及到了另一个问题: proto 和 prototype

在 JavaScript 原型继承结构里面,规范中用 [[Prototype]] 表示对象隐式的原型,在 JavaScript 中用 proto 表示,

  • 对象有属性__proto__,指向该对象的构造函数的原型对象。
  • 方法除了有属性__proto__,还有属性prototype,prototype指向该方法的原型对象。

而对于原型链有几点需要注意的:

1.关于constructor

在默认情况下,所有原型对象都会自动获得一个 constructor(构造函数)属性,这个属性包含一个指向 prototype 属性所在函数的指针。例如,Person.prototype.constructor 指向 Person。而通过这个构造函数,我们还可继续为原型对象添加其他属性和方法。

虽然可以通过对象实例访问保存在原型中的值,但却不能通过对象实例重写原型中的值
如果我们在实例中添加了一个属性,而该属性与实例原型中的一个属性同名,那我们就在实例中创建该属性,该属性将会屏蔽原型中的那个属性。来看下面的例子。

function Person(){}

Person.prototype.name = "Stone";
Person.prototype.age = 28;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
    console.log(this.name);
};

var person1 = new Person();
var person2 = new Person();

person1.name = "Sophie";
console.log(person1.name);     // "Sophie",来自实例
console.log(person2.name);     // "Stone",来自原型

在这个例子中,person1 的 name 被一个新值给屏蔽了。但无论访问 person1.name 还是访问 person2.name 都能够正常地返回值,即分别是 “Sophie”(来自对象实例)和 “Stone”(来自原型)。当访问 person1.name 时,需要读取它的值,因此就会在这个实例上搜索一个名为 name 的属性。这个属性确实存在,于是就返回它的值而不必再搜索原型了。当访问 person2. name 时,并没有在实例上发现该属性,因此就会继续搜索原型,结果在那里找到了 name 属性。
当为对象实例添加一个属性时,这个属性就会屏蔽原型中保存的同名属性;换句话说,添加这个属性只会阻止我们访问原型中的那个属性,但不会修改那个属性。即使将这个属性设置为 null ,也只会在实例中设置这个属性,而不会恢复其指向原型的连接。
不过,使用 delete 操作符则可以完全删除实例属性,从而让我们能够重新访问原型中的属性。

2.constructor属性其实就是一个拿来保存自己构造函数引用的属性,没有其他特殊的地方。

思考个问题:new Person( ) 出来的千千万万个实例中如果都有constructor属性,并且都指向创建自己的构造函数,那它们都拥有一个效果相同但却都各自占用一部分内存的属性?

onstructor是完全可以被当成一个共享属性存放在原型对象中,作用也依然是指向自己的构造函数,而实际上也是这么处理的。对象的constructor属性就是被当做共享属性放在它们的原型对象中

但是如果是共享属性,那我将两个实例其中一个属性改了,为啥第二个实例没同步?如下面代码:

function Person() {}
var person1 = new Person()
var person2 = new Person()
console.log(person1.constructor) // [Function: Person]
console.log(person2.constructor) // [Function: Person]
person1.constructor = Function
console.log(person1.constructor) // [Function: Function]
console.log(person2.constructor) // [Function: Person] !不是同步为[Function: Function]

这个是因为person1.constructor = Function改的并不是原型对象上的共享属性constructor,而是给实例person1加了一个constructor属性。

console.log(person1) // 结果:Function { constructor: [Function: Function] }

我们根本不能通过一个对象.constructor找回创建自己的构造函数(之间没有箭头链接)!

function Person() {}
console.log(Object instanceof Object);     //true
//第一个Object的原型链:Object=>
//Object.__proto__ => Function.prototype=>Function.prototype.__proto__=>Object.prototype
//第二个Object的原型:Object=> Object.prototype

console.log(Function instanceof Function); //true
//第一个Function的原型链:Function=>Function.__proto__ => Function.prototype
//第二个Function的原型:Function=>Function.prototype

console.log(Function instanceof Object);   //true
//Function=>
//Function.__proto__=>Function.prototype=>Function.prototype.__proto__=>Object.prototype
//Object => Object.prototype

console.log(Person instanceof Function);      //true
//Person=>Person.__proto__=>Function.prototype
//Function=>Function.prototype

console.log(String instanceof String);   //false
//第一个String的原型链:String=>
//String.__proto__=>Function.prototype=>Function.prototype.__proto__=>Object.prototype
//第二个String的原型链:String=>String.prototype

console.log(Boolean instanceof Boolean); //false
//第一个Boolean的原型链:Boolean=>
//Boolean.__proto__=>Function.prototype=>Function.prototype.__proto__=>Object.prototype
//第二个Boolean的原型链:Boolean=>Boolean.prototype

console.log(Person instanceof Person); //false[添加链接描述](https://juejin.im/post/5ced143cf265da1bcb4f0d06#heading-11)
//第一个Person的原型链:Person=>
//Person.__proto__=>Function.prototype=>Function.prototype.__proto__=>Object.prototype
//第二个Person的原型链:Person=>Person.prototype

所以再回顾原题目 答案不难理解了

参考文章

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值