Javascript的 __proto__和 prototype和原型链

__proto__

JavaScript的每一个对象(null除外)都和另一个对象关联,而这里说的另一个对象便是原型。每个对象都从原型继承属性以及方法。
如何确定一个对象关联的另一个对象是哪个呢?
这就涉及到JavaScript对象的一个内部属性[[Prototype]] ,这个属性指向的便是当前对象关联的(或者说继承的)另一个对象。我们是不能访问内部属性的,
  我们可以通过 __proto__来访问,但是最新的web标准已经删除了它,更推荐使用Object.getPrototypeOf/Reflect.getPrototypeOf 和Object.setPrototypeOf/Reflect.setPrototypeOf,这两对方法来读写[[Prototype]],

Object.create 可以在创建对象的时候就指定其原型。

每个JavaScript对象的__proto__ 属性指向该对象的原型,一连串的__proto__ 组成了原型链。null是所有原型链的终点。

prototype

这个是函数的原型属性,它指向函数的原型对象。这个属性有什么用呢?或者说函数的原型对象是干嘛的,

function person(){
	console.log(1);
}

函数的调用有四种方法:

  1. 作为函数 person()
  2. 作为方法 xxx.person()
  3. 作为构造函数 new person()
  4. 通过他们的call()或者apply()方法间接调用 person.call(this);person.apply(this)

我们通过第1、2、4这三种方法调用函数的时候,函数的prototype属性并没有什么用。
当我们 将函数作为构造函数调用的时候。原型对象属性(prototype)就有用了。之前我们说过每个对象都有__proto__属性或者说内部属性[[Prototype]] ,而函数作为构造函数调用的时候会返回一个对象,那返回的这个对象的__proto__指向哪里呢?它就指向构造函数person()的prototype属性。

原型对象中包含所有实例共享的属性和方法。原型对象中存在一个属性constructor指向构造函数。

任何JavaScript函数都可以当做构造函数,所以每个JavaScript函数都会有一个prototype属性(Function.bind()创建的函数以及箭头函数除外)。

关于构造函数创建对象。

  1. 构造函数就是用来构造新对象,所以构造函数调用会创建一个新的空对象,这个对象继承自构造函数的prototype属性。构造函数初始化这个新创建的对象,并将这个对象用做其调用上下文。因此构造函数可以使用this来引用这个新创建的对象。构造函数看起来像方法调用,但是它会使用这个新对象作为调用上下文,例如 new person()我们在person函数中使用this它并不指向window,而是指向新创建的对象。
  2. 构造函数通常不使用return关键字,它通常初始化新对象。当构造函数执行完毕时,它会显式返回这个对象。所以说构造函数调用表达式的计算结果就是这个对象。如果构造函数显式使用return返回一个对象,那么调用表达式的值就是这个对象。如果构造函数使用return语句但是没有返回值,或者返回一个原始值,这时返回值将会被忽略,同时依旧使用创建新对象作为调用的结果。
var name = 'Nick';
function person(){
	console.log(this.name);
}
person();  // Nick   此时其上下文为window 也就是this指向了window,所以访问到了Nick
new person(); // 'undefined'  // 此时this指向新创建的空对象,而这个空对象没有name属性所以输出了undefind   因为我们在构造函数中没有对这个空对象做任何操作,所以它只有一个属性那就是 `__proto__`指向person.prototype
person.prototype.name = "Tom"
new person(); // Tom 此时通过this沿着继承链找到了prototype上面的name属性所以就输出了 Tom

说到这里我们可以模拟实现下new操作符做的操作。

// 第一个入参为构造函数,其余为构造函数的参数,
function n(){
     var obj = new Object();  // 此时obj只有一个属性 __proto__ 指向 Object.prototype 
     var constructor = Array.prototype.shift.call(arguments); // 从auguments拿到构造函数,同时arguments中删除构造函数,
     obj.__proto__ = constructor.prototype; // 将obj的__proto__ 指向构造函数的prototype
     // 使用新创建的对象作为构造函数的上下文,调用构造函数
     var result = constructor.apply(obj,arguments);  // 构造函数初始化这个新对象,
     return typeof result === 'object' ? result : obj;
     //如果构造函数显式使用return返回一个对象,那么调用表达式的值就是这个对象。如果构造函数使用return语句但是没有返回值,或者返回一个原始值,这时返回值将会被忽略,同时使用这个新对象作为调用的结果。
}
var temp = n(perosn);
// 等同于
var temp = new person();

// 鉴于__proto__要被弃用,我们写个新的

function n(){
  const obj = new Object(); // 创建一个空对象
  const constructor = Array.prototype.shift.call(arguments); // 从参数里面获取构造的函数
  obj.constructor = constructor; // 对象的constructor属性指向构造函数
  // 将构造函数的原型对象 设置给该对象
  Reflect.setPrototypeOf(obj,constructor.prototype);
  const result = constructor.apply(obj,arguments);
  return typeof result === 'object' ? result : obj;
}

function person(){
  this.name = 12;
}

const me = n(person);
console.log(me);

原型链

JavaScript 常被描述为一种基于原型的语言 (prototype-based language)——每个对象拥有一个原型对象,对象以其原型为模板、从原型继承方法和属性。原型对象也可能拥有原型,并从中继承方法和属性,一层一层、以此类推。这种关系常被称为原型链 (prototype chain),它解释了为何一个对象会拥有定义在其他对象中的属性和方法。
在传统的 OOP 中,首先定义“类”,此后创建对象实例时,类中定义的所有属性和方法都被复制到实例中。在 JavaScript 中并不如此复制——而是在对象实例和它的构造器之间建立一个链接(它是__proto__属性,是从构造函数的prototype属性派生的),之后通过上溯原型链,在构造器中找到这些属性和方法。

其他OOP语言中类实例化的时候,会把类中定义的属性方法复制给实例一份,而JavaScript中"类"在实例化的时候,只是给了实例一个__proto__ 属性让JavaScript实例对象通过,这个属性找到类定义的属性和方法。从而可以操作和调用“类”的属性和方法。

function Person(){}
var person1 = new Person();
person1.valueOf();

以访问person1的valueOf为例:

  1. 浏览器首先检查,person1 对象是否具有可用的 valueOf() 方法。
  2. 如果没有,则浏览器检查 person1 对象的原型对象(即person1.__proto__指向的对象也是 Person构造函数的prototype属性所指向的对象)是否具有可用的 valueof() 方法。
  3. 如果也没有,则浏览器检查 Person() 构造函数的prototype属性所指向的对象的原型对象(即person1.__proto__.__proto__指向的对象也就是 Object构造函数的prototype属性所指向的对象)是否具有可用的 valueOf() 方法。这里有这个方法,于是该方法被调用。
  4. 如果还是没有。则会到Object构造函数的prototype属性所指向的对象的原型对象也就是null,没得找所以就是undefined。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值