_proto_和prototype的关系以及区别

259 阅读4分钟

__proto__严格意义上并不是一个属性

__proto__严格意义上并不是一个属性,而是getter和setter进行是否为对象来判断是否取或者设置值的

如果设置对象

let obj = {}
  obj.__proto__ = {
    name: 'han'
  }
  console.log(obj.__proto__);//han

此时有继承对象里面有个属性name为han

如果设置基本数据类型

let obj = {}
obj.__proto__ = "嘻嘻"
console.log(obj.__proto__);

此时原型并没有 "嘻嘻"这个属性

我们再认真看下proto

此时我们可以确定proto利用了getter和setter来判断是否为对象来设置的

继承的方法

1.Object.setPrototypeOf
   let obj = {
    name: 'han'
  }  
  let tem2 = {
    name: '韩',
    show: function() {
      console.log(this.name);
    }
  }
  Object.setPrototypeOf(obj,tem2)
  console.log(obj.__proto__ === tem2);//true
  
  
2. 用es6语法糖class
class A{}
class B extends A{}

console.log(B.__proto__.__proto__ === A.__proto__);//true
console.log(B.__proto__ === A);//true

3.直接.__proto__
function A(){}
function B(){}
A.__proto__ = B;

4.利用Object.create
let obj = {}
let hj = {
    show: function(){
      console.log('你被继承啦~');
    }
  }
obj = Object.create(hj)
console.log(obj.__proto__ === hj);//true

此方法只能定义原型,不能获取后面追加的属性。例如:、
let obj = {}
  let hj = {
    show: function(){
      console.log('你被继承啦~');
    }
  }
  obj = Object.create(hj,{
    home:{
      wen: 'home'
    }
  })
  console.log(Object.getPrototypeOf(obj));//只能获取到show方法而无法拿到home属性

_proto_是服务于函数对象的,prototype是服务于构造函数的实例化对象的

js的函数会有一个默认的prototype对象,这个prototype对象用来指定函数的继承关系,prototype对象默认有两个属性,一个是constructor,另一个就是proto属性,默认的prototype对象在被改变之前就像是这个函数用来表示自己的一个对象,其中proto属性和普通对象的proto属性一样用来对应继承关系

  console.log(User.__proto__ === Object);//false
  console.log(User.__proto__ === Object.__proto__);//true
  console.log(User.prototype.__proto__ === User.__proto__.__proto__);//true
  console.log(User.prototype.__proto__ === Object.prototype);//true
  

当我们只设置_proto_时

function User(){};
  User.__proto__.view = function(){
    console.log("User View method");
  };
  /* 
  
    User本身能够像对象一样调用原型的属性,说明_proto_是服务于函数对象的
    hd是User的实例化对象,但是此时报错未定义。
    说明只有User的prototype设置的属性才可以使用 
    说明prototype是服务于构造函数的实例化对象的
   */
  User.view();//User View method

  let hd = new User();
  hd.view();//Uncaught TypeError: hd.view is not a function

当我们设置prototype:

function User(){};
  
  User.prototype.name = "houdunren"
  let hd = new User();
  /*
      说明只有User的prototype设置的属性才可以使用 
      说明prototype是服务于构造函数的实例化对象的
  */
  console.log(hd.name);//houdunren

此时的的指向图为:

(插一下小知识点:但我们设置了

User.__proto__.view = function(){
    console.log("User View method");
  };

之后,Object的原型也会有这个view函数 此时打印一下Object:

console.dir(Object);

结果:Object的原型也会有这个view函数

但是同样设置prototype.name的prototype不会有这个实例,这也再次说明了prototype是为创建的对象实例服务的:

此时我们输入:

console.log(User.prototype.__proto__ === Object.prototype);//true
console.log(User.__proto__ === Object.__proto__);//true

此时的图说明

)

原博客:www.cnblogs.com/onepixel/p/…

这篇博客讲的也很好!!:blog.csdn.net/u013448536/…

在Javascript中,万物皆对象,但对象也有区别,大致可以分为两类,即:普通对象 Object 和 函数对象 Function。

function f1() {
    // todo
}
varf2 = function () {
    // todo
};
varf3 = newFunction('x',  'console.log(x)');
 
varo1 = {};
varo2 = newObject();
varo3 = newf1();
 
console.log(
    typeoff1,
    typeoff2,
    typeoff3,
    typeofo1,
    typeofo2,
    typeofo3
);

在 JS 中,每当创建一个函数对象 f1 时,该对象中都会内置一些属性,其中包括 prototype 和 proto, prototype 即原型对象,它记录着f1的一些属性和方法。

需要注意的是,prototype 对 f1 是不可见的,也就是说,f1 不会查找 prototype 中的属性和方法。

function f(){}
f.prototype.foo = "abc";
console.log(f.foo); // undefined

那么,prototype 有什么用呢? 其实 prototype 的主要作用就是继承。 通俗一点讲,prototype 中定义的属性和方法都是留给自己的 “后代” 用的,因此,子类完全可以访问prototype中的属性和方法。

想要知道 f1 是如何把 prototype 留给“后代”,我们需要了解一下 JS 中的原型链。此时,JS中的 proto 入场了,它存在于普通对象和函数对象中,它的作用就是引用父类的 prototype 对象,JS在通过 new 操作符创建一个对象的时候,通常会把父类的 prototype 赋值给新对象的 __proto__属性,这样就形成了一代代传承...

function f() {}
f.prototype.foo = "abc";
varobj = newf();
console.log(obj.foo); // abc

如图所示,f.prototype 的 proto 中保存的是 Object.prototype,Object.prototype 对象中也有 proto,而从输出结果看,Object.prototype.proto 是 null,表示 obj 对象原型链的终结。如下图所示:




总结

  • 原型链的形成真正是靠__proto__ 而非prototype,当JS引擎执行对象的方法时,先查找对象本身是否存在该方法,如果不存在,会在原型链上查找,但不会查找自身的prototype。

  • 一个对象的 proto 记录着自己的原型链,决定了自身的数据类型,改变 proto 就等于改变对象的数据类型。

  • 函数的 prototype 不属于自身的原型链,它是创建子类的核心,决定了子类的数据类型,是连接子类原型链的桥梁。 在原型对象上定义方法和属性,是为了被子类继承和使用。

  • 改变原型跟this指向没有关系,例如:

let obj = {
    name: 'han'
  }  
  let tem2 = {
    name: '韩',
    show: function() {
      console.log(this.name);
    }
  }
  Object.setPrototypeOf(obj,tem2)
  console.log(obj.show());//输出han,此时虽然obj原型是tem2并且调用的是来自于tem2的show方法,但此时this还是指向调用者obj