lznbuild / my-blog

自己的博客
9 stars 1 forks source link

JavaScript从原型到原型链 #2

Open lznbuild opened 4 years ago

lznbuild commented 4 years ago

原型

下图是关于原型,原型链最完整的图,先把它放出来镇楼。。。不着急,下面会慢慢的去理这个图的关系。

要理清那个大图,首先我们要知道构造函数是啥。

构造函数就是可以创建特定类型对象的函数

下面给一个示例,这个示例在本篇文章中一直用。

function Foo(name) {
    this.name = name
}

Foo.prototype.sayName = ()=> {
    console.log(this.name)
}
var f1 = new Foo('f1'); // new 了一个Foo的实例,f1
console.log(f1);

var f2 = new Foo('f2'); // new 了一个Foo的实例,f2
console.log(f2);

console.log(Foo.prototype)

下面是打印结果

通过查阅资料,对结果做一下总结:

Foo是一个构造函数,大写首字母是规范,f1,f2 是Foo的实例。

先看f1,f2,都有一个_proto_属性。

所有对象,都有一个隐式引用(非开发者创建)[[prototype]]

chrome浏览器中用_proto_表示这个属性,以下都沿用这一命名。

f1._proto_和 f2._proto_指向一个原型对象,再看Foo,有一个Prototype属性,只要创建新函数,就会给该函数创建这个属性,Foo.prototype也指向一个原型对象,它俩指向同一个原型对象。Foo.prototype上有一个constructor属性,是一个指向prototype所在的函数的指针,这里自然指向了Foo,所有Foo实例都会共享Foo.prototype上的属性和方法

以上总结建议多读几遍,用心感悟。。。

代码验证一下

f1._proto_ === Foo.prototype // true
f2._proto_ === Foo.prototype // true
f1._proto_ === f2._proto_ // true ,都是同一个对象的引用
Foo.prototype.constructor === Foo // true

这部分对应大图的这一部分。

同理,图中部分也解释的通,只是把我们声明的Foo构造函数换成了原生JS中的Object和Function构造函数。需要注意的点是,Object.prototype._proto_指向了null,可以理解为原型链的终点就是null。

然后,再看Foo.prototype,先前说了它指向原型对象,原型对象也是对象,是对象就有_proto_属性,而且对象都是原生对象Object的实例,所以得出下面结论。

Foo.prototype._proto_ === Object.prototype // true
Object.prototype._proto_ === null // true 这个比较特殊

图中这部分也解释通了

在Javascript中,函数也属于对象,不管函数是通过new Function的实例还是function关键字声明的函数,也会有_proto_属性,指向Function.prototype。

解释了下图的红线表记的部分。

至此,大图的所有逻辑都走了一遍。

没搞明白的上滑

原型链

f1,f2 不光可以调用Foo.prototype上的方法,还可以调用Object.prototype上的方法,看图,由_proto_组成的链条就是原型链。

顺便看一个ES5的方法,可以获得,设置对象的原型,代替obj.proto 这种写法,这种写法不规范,有的浏览器不把[[prototype]]用_proto_表示。

Object.getPrototypeOf(obj)  // 获取obj的原型对象  
Object.setPrototypeOf(obj,prototype)  // 设置obj的原型对象为prototype

规避特性

当获取一个对象上的属性时,会先从此对象上查找,如果没找到,就去原型上找,找到了就返回此属性值,如果原型上有,就返回此属性值,如果没有,就是undefined。

function Parent() {}
Parent.prototype.name="ParentName";
var parent1 = new Parent();
parent1.name = 'parentName';
console.log(parent1.name) // parentName
parent1.name = 'parentName22'; //修改的也是实例上的属性,非原型对象上的属性
console.log(parent1.name) // parentName22

可以用hasOwnProperty判断属性来自实例还是原型

function Parent(){}
Parent.prototype.attr='123';
var p = new parent();
p.name = 'pName';
console.log(p.hasOwnProperty('name')) // true,来自实例。
console.log(p.hasOwnProperty('attr')) // false,来自原型对象

遍历对象属性的方法

for in 可以遍历对象本身和原型上的属性,只要其可枚举。
Object.keys() 只能遍历对象本身的可枚举属性。
Object.getOwnPropertyNames() 遍历实例上所有属性,不管是否可枚举。

new 关键字

new Foo和new Foo(),两种调用方式,除了传参数的区别,看起来做了同样的事?

是做了同样的事,但这里有坑

function Foo() {
 return this;
}

Foo.getName = function () {
 console.log('1');
};
Foo.prototype.getName = function () {
 console.log('2');
};
new Foo.getName(); // -> 1
new Foo().getName(); // -> 2 
//new Foo() 的优先级大于 new Foo ,所以对于上述代码来说可以这样划分执行顺序
//new (Foo.getName());  // -> 1
//(new Foo()).getName();  // -> 2

实现一个new


function create() {
    // 创建一个空的对象
    var obj = new Object(),
    // 获得arguments中第一个参数,并在arguments中去除
    Con = [].shift.call(arguments);
  // 链接到原型,obj 可以访问到构造函数原型中的属性
  Object.setPrototypeOf(obj, Con.prototype)
    // 绑定 this 实现继承,obj 可以访问到构造函数中的属性
    var ret = Con.apply(obj, arguments);
    // 优先返回构造函数返回的对象,非对象仍然返回实例,new就是这样
  return ret instanceof Object ? ret : obj;
  // 如果[[call]]的返回值是对象,那么,返回这个对象,否则返回第一步创建的新对象
};

验证

function Fn(name){
    this.name=name
}

Fn.prototype.sayName=function(){console.log(this.name)}

var fn1 = create(Fn,'fn1-name')

完美!

最后,做一道非常全面的题来看看是否理解了new关键字,原型的关系。

  function Foo() {

    getName = function () {

      console.log(1);

    }

    return this;

  }

  Foo.getName = function () {

    console.log(2);

  }

  Foo.prototype.getName = function () {

    console.log(3);

  }

  var getName = function () {

    console.log(4);

  }

  function getName() {

    console.log(5);

  }

  Foo.getName(); // 2

  getName(); // 4

  Foo().getName(); // 1

  getName(); // 1

  new Foo.getName(); //2   new (Foo.getName)()

  new Foo().getName(); // 3 (new Foo()).getName()

  new new Foo().getName(); // 3  new ((new Foo()).getName)()

后面3个都是优先级的问题

解析请看下面的链接,毕竟题都是看的大佬的。。。

http://asyncnode.com/blog/InterviewQuestion/new-javascript.html

这部分写的很糙,是因为《高程》上写的实在是太好了,建议直接看《高程》