浅谈:疾速明白JS的原型与原型链

JavaScript中有基础范例和庞杂范例的辨别。

当我们在声明一个基础范例时:

var n1= 1;
console.log(n1);
//1

这时候我们能够用Number要领将1包装为对象,即声明一个对象1。

var n2= new Number(1);
console.log(n2);
//Number {1}
//[[PrimitiveValue]]:1

//△__proto__: Number

//constructor: ƒ Number()
//toExponential: ƒ toExponential()
//toFixed: ƒ toFixed()
//toLocaleString: ƒ toLocaleString()
//toPrecision: ƒ toPrecision()
//toString: ƒ toString()
//valueOf: ƒ valueOf()
//__proto__: Object

n2的PrimitiveValue(初始值)为1,实在此时它就是一个hash
此时对象n2如今有许多要领,能够挪用对应各自的函数。

然则,我们发现有一点:在我们直接声明基础数据范例n1时,也能够运用这些要领挪用函数。
比方toString要领:

var n1= 1;
n1.toString();
//"1"

这就要涉及到JavaScript的发现汗青。在设想之初,它被请求:“长得像” Java
当时的Java声明一个对象,就是云云:

var n= new Number(1)

设想师在设想的同时,为了轻便快速,也制订了我们最经常使用的声明体式格局:

var n= 1

固然,这类要领一定被JavaScript程序员所喜欢,第一种要领险些没有人用。
然则有一个题目,假如直接声明n,那末它就是一个数字。而基础数据范例是没有属性的。
这时候候该怎样调取种种要领呢?

声明一个暂时对象,我们暂将它称为temp
比方在对n举行toString要领援用时,会声明一个暂时对象,对n举行庞杂封装,将其变成对象。

var n= 1;
n.toString();

实际上是:

var temp= new Number(n);
temp.toString();
//1

将temp.toString的值给予n.toString。在操纵完毕后,暂时对象将从内存中抹除

共用属性

number,string,boolean,object都有一些配合的要领,比方toStringvalueof等等。为了防止反复声明和内存糟蹋,这些要领“归结”与一个共用属性当中。

JavaScript在声明一个对象后,并非先默许复制一遍共用属性在内存中的寄存地点,再在援用时调取对应的函数。而是:

在声明对象时,就存入一个隐蔽的属性:__proto__。该属性,对应其共用属性。

var obj= {
  name: 'Jack',
  age: 18
};
console.log(obj);
//△{name:"Jack", age:18}
  //age: 18
  //name: "Jack"
  //△__proto__: Object
    //constructor: ƒ Object()
    //hasOwnProperty: ƒ hasOwnProperty()
    //isPrototypeOf: ƒ isPrototypeOf()
    //propertyIsEnumerable: ƒ propertyIsEnumerable()
    //toLocaleString: ƒ toLocaleString()
    //toString: ƒ toString()
    //valueOf: ƒ valueOf()
    //__defineGetter__: ƒ __defineGetter__()
    //__defineSetter__: ƒ __defineSetter__()
    //__lookupGetter__: ƒ __lookupGetter__()
    //__lookupSetter__: ƒ __lookupSetter__()
    //get __proto__: ƒ __proto__()
    //set __proto__: ƒ __proto__()

那我们在挪用toString要领时:
JavaScript首先会搜检数据范例是不是是对象;若不是,则包装为对象作暂时的转换。
然后,在搜检对象中是不是有toString这个属性;若没有,才进入共用属性举行搜检

两个空对象是不相称的,但他们的共有属性是相称的。

var obj1= {};
console.log(obj1);
//{}
var obj2= new Object();
console.log(obj2);
//{}
obj1 === obj2;
//false
obj1.__proto__ === obj2.__proto__;
//true

然则,当我们声明一个number为对象时,它就有本身区分于平常对象的奇特的共有属性。
比方toFixedtoExponential等等。
那末它的__proto__属性就对应本身独占的配合属性,在配合属性中另有另一个隐蔽属性__proto__对应平常对象的共有属性。如许,number范例的对象就能够挪用一切的函数。

原型与原型链

这里,就引入了两个新的观点。
那末,这个共有属性,就被称为原型(对象)。原型对象就是用来寄存声明对象中共有的那部分属性。
JavaScript中一切的对象都能够继续其原型对象的属性。
而原型对象本身也是一个对象,它也有本身的原型对象。如许层层上溯,就形成了一个相似链表的构造,这就是原型链

为了防止对原型对象在没有被运用时被内存清算,JavaScript经由过程prototype来默许援用。

Object.prototype

Object.prototype;

//constructor: ƒ Object()
//hasOwnProperty: ƒ hasOwnProperty()
//isPrototypeOf: ƒ isPrototypeOf()
//propertyIsEnumerable: ƒ propertyIsEnumerable()
//toLocaleString: ƒ toLocaleString()
//toString: ƒ toString()
//valueOf: ƒ valueOf()
//__defineGetter__: ƒ __defineGetter__()
//__defineSetter__: ƒ __defineSetter__()
//__lookupGetter__: ƒ __lookupGetter__()
//__lookupSetter__: ƒ __lookupSetter__()
//get __proto__: ƒ __proto__()
//set __proto__: ƒ __proto__()

Number.prototype

Number.prototype;

//constructor: ƒ Number()
//toExponential: ƒ toExponential()
//toFixed: ƒ toFixed()
//toLocaleString: ƒ toLocaleString()
//toPrecision: ƒ toPrecision()
//toString: ƒ toString()
//valueOf: ƒ valueOf()
//__proto__: Object
//[[PrimitiveValue]]: 0

云云,另有:
String.prototype
Boolean.prototype
等等。

Object.prototype.__proto__ === null

我们制造一个简朴的示意图,以便更直观地明白这些观点。

《浅谈:疾速明白JS的原型与原型链》

那末我们能够获得关联:

var obj= {};
obj.__proto__ === Object.prototype;
//true

var n= new Number(1);
n.__proto__ === Number.prototype;
//true
n.__proto__.__proto__ === Object.prototype;
//true

总结起来,获得的结论就是:

  • Object.prototypeObject的共用属性
  • obj.__proto__Object的共用属性的援用
  • 同理,关于StringBooleanSymbolNumber也是云云
    原文作者:南屋流星
    原文地址: https://segmentfault.com/a/1190000017458538
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞