一.导言介绍构造函数、原型和原型链。比如经常问symbol是不是构造函数;构造函数属性是否是只读的;原型、[[Prototype]]和_ _ prototype _ _的区别;什么是原型链?等一下。二、构
介绍构造函数、原型和原型链。比如经常问symbol是不是构造函数;构造函数属性是否是只读的;原型、[[Prototype]]和_ _ prototype _ _的区别;什么是原型链?等一下。
二、构造函数1.什么建造师?
函数是通过new关键字生成实例的函数。
js的构造函数不同于其他语言。一般第一个字母大写。
首先,我们来看看这个栗子:
// saucxsfunction Parent(age) { this.age = age;}var p = new Parent(30);console.log(p); //见下图console.log(p.constructor); // ? Parent(age){this.age = age;}p.constructor === Parent; // truep.constructor === Object; // false
这是一个典型的构造函数。构造函数本身也是一个函数,和普通函数区别不大。主要区别是构造函数使用new生成实例,直接调用的是普通函数。
2.构造函数属性
返回对创建实例对象的对象构造函数的引用。该属性的值是对函数本身的引用,而不是包含函数名称的字符串。
所有对象都从其原型继承构造函数属性:
var o = {};o.constructor === Object; // truevar o = new Object;o.constructor === Object; // truevar a = [];a.constructor === Array; // truevar a = new Array;a.constructor === Array // truevar n = new Number(3);n.constructor === Number; // true
那么普通函数创建的实例有构造函数属性吗?
// saucxs// 普通函数function parent2(age) { this.age = age;}var p2 = parent2(50);console.log(p2);// undefined// 普通函数function parent3(age) { return { age: age }}var p3 = parent3(50);console.log(p3.constructor); //? Object() { [native code] }p3.constructor === parent3; // falsep3.constructor === Object; // true
上面的代码解释了:
(1)内部有返回操作的普通函数有构造函数属性,没有返回的没有构造函数属性;
(2)带有构造函数属性的普通函数的构造函数属性的值不是普通函数本身,而是对象。
3.symbol是构造函数吗?
MDN引入了这样的符号。
` Symbol()`函数返回**symbol**类型的值,具有公开内置对象的几个成员的静态属性,具有公开全局符号注册表的静态方法,类似于内置对象类,但作为构造函数是不完整的,因为它不支持语法" ` new Symbol()` "。
符号是一种基本数据类型。作为构造函数,它是不完整的,因为不支持语法new Symbol()。如果你想生成一个实例,你可以直接使用Symbol()。
// saucxsnew Symbol(123); // Symbol is not a constructorSymbol(123); // Symbol(123)
尽管Symbol是一种基本数据类型,但Symbol(1234)实例可以获取构造函数属性的值。
// saucxsvar sym = Symbol(123); console.log( sym ); // Symbol(123)console.log( sym.constructor ); // ? Symbol() { [native code] }sym.constructor === Symbol; //truesym.constructor === Object; //false
这里的构造函数属性从何而来?其实在符号原型上,默认是符号函数。
4.构造函数的值是只读的吗?
答:如果是引用类型,可以修改构造函数属性的值;如果是基本类型,则它是只读的。
在引用的情况下,修改这个就很好理解了。例如,在原型链继承的方案中,它是对构造函数的新赋值的修改。
// saucxsfunction Foo() { this.value = 42;}Foo.prototype = { method: function() {}};function Bar() {}// 设置 Bar 的 prototype 属性为 Foo 的实例对象Bar.prototype = new Foo();Bar.prototype.foo = 'Hello World';Bar.prototype.constructor === Object; //true// 修正 Bar.prototype.constructor 为 Bar 本身Bar.prototype.constructor = Bar;var test = new Bar() // 创建 Bar 的一个新实例console.log(test);
基本类型只读,如:1,“saucxs”,true,symbol,null,undefined。而null和undefined也没有构造函数属性。
// saucxsfunction Type() { };vartypes = [1, "muyiy", true, Symbol(123)];for(var i = 0; i < types.length; i++) {types[i].constructor = Type;types[i] = [ types[i].constructor, types[i] instanceof Type, types[i].toString() ];};console.log( types.join("n") );// function Number() { [native code] },false,1// function String() { [native code] },false,muyiy// function Boolean() { [native code] },false,true// function Symbol() { [native code] },false,Symbol(123)
为什么会这样?因为它们是由只读的本机构造函数创建的,所以这个例子表明依赖对象的构造函数属性是不安全的。
三。原型3.1原型属性
每个对象都有一个原型对象,原型对象以其原型为模板,集成了来自原型的方法和属性。这些类和方法在对象的构造函数的原型属性上,而不是在对象实例本身上。
上图表明:
1.父对象有一个原型对象Parent.prototype,它有两个属性,分别是:constructor和__proto__,其中__proto__已被弃用。
2.构造函数父级有一个指向原型的指针构造函数;原型Parent.prototype有一个指向构造函数parent . prototype . constructor的指针,实际上是一个循环引用。
3.2 __proto__属性
如上图所示,父原型(Parent.prototype)有一个__proto__属性,这是一个访问器属性(即getter函数和setter函数)。函数:你可以通过__proto__访问一个对象[[Prototype]](一个对象或者null)的内部
`__proto__ `读作dunder proto,最初由Firefox使用,后来在ES6中被列为Javascript的标准内置属性。
`[[[Prototype]]`是对象的内部属性,不能由外部代码直接访问。
// saucxsfunction Parent(){}; var p = new Parent();console.log(p);console.log(Parent.prototype);
1.p.__proto__获取对象的原型,__proto__是存在于每个实例中的属性;
2.原型是构造函数的属性;
3.p.__proto__和Parent.prototype指向同一个对象。
// saucxsfunction Parent() {}var p = new Parent();p.__proto__ === Parent.prototype// true
因此,构造函数Parent、Parent.prototype和p之间的关系如下图所示:
1:仅当“es6”时,“_ _ proto _ _”属性才被标准化。
以确保Web浏览器的兼容性,但不建议这样做。除了标准化,还有性能问题。为了获得更好的支持,建议使用Object.getPrototypeOf()'。
如果要读取或修改对象的`[[prototype] ]`属性,建议使用以下方案。然而,此时设置对象的`[[prototype] ]`仍然是一个缓慢的操作。如果性能有问题,应该避免此操作。
如果您想要创建一个新对象并从另一个对象继承`[ [[Prototype]] ]`,建议使用` Object.create()`。
// saucxsfunction Parent() { age: 50};var p = new Parent();var child = Object.create(p);
这里child是一个新的空对象,它有一个指向对象p的指针`_ __proto__ `。
四。原型链每个对象都有一个原型对象,它通过__proto__指针指向前一个原型,并从中继承方法和属性。同时,原型对象也可能有原型,这样它们的层最终指向null。这种关系叫做原型链,它的作用是:通过原型链,一个对象会有在其他对象中定义的属性和方法。
// saucxsfunction Parent(age) { this.age = age;}var p = new Parent(50);p.constructor === Parent; // true
P.constructor指向Parent,那么是否意味着P实例化中存在构造函数属性?不存在,打印p:
从图中可以看出,实例化的对象P本身没有构造函数属性。就是通过原型链搜索__proto__最后找到构造函数属性,指向父级。
// saucxsfunction Parent(age) { this.age = age;}var p = new Parent(50);p;// Parent {age: 50}p.__proto__ === Parent.prototype; // truep.__proto__.__proto__ === Object.prototype; // truep.__proto__.__proto__.__proto__ === null; // true
下图显示了原型链操作机制。
动词 (verb的缩写)摘要1.Symbol是基本数据类型,作为构造函数是不完整的,因为不支持语法new Symbol(),但是原型有构造函数属性,即Symbol.prototype.constructor
2.引用类型构造函数属性值可以修改,但对于基本类型是只读的。当然,null和undefined没有构造函数属性。
3.__proto__是存在于每个实例上的属性,prototype是构造函数的属性。这两个是不同的,但是p.__proto__和Parent.prototype指向同一个对象。
4.__proto__属性在ES6中是标准化的,但由于性能问题,不建议使用。建议使用Object.getPropertyOf()。
5.每个对象都有一个prototype对象,它通过__ptoto_ pointer指向前一个原型,并从中继承方法和属性。同时,原型对象也可能有原型,这样这一层已经完成,最终指向null,这就是原型链。