Products
GG网络技术分享 2025-03-18 16:13 0
介绍构造函数,原型,原型链。比如说经常会被问道:symbol是不是构造函数;constructor属性是否只读;prototype、[[Prototype]]和__proto__的区别;什么是原型链?等等问题
构造函数就是通过new关键词生成实例的函数。
js的构造函数和其他语言不一样,一般规范都是首字母大写。
首先我们来看一下这个栗子:
// saucxs
function 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; // true
p.constructor === Object; // false
这就是一个典型的构造函数,构造函数本身也是个函数,与普通区别不大,主要区别就是:构造函数使用new生成实例,直接调用就是普通函数。
2、constructor属性返回创建实例对象的Object构造函数的引用。此属性的值对函数本身的引用,而不是一个包含函数名称的字符串。
所有对象都会从它的原型上继承一个constructor属性:
var o = {};
o.constructor === Object; // true
var o = new Object;
o.constructor === Object; // true
var a = [];
a.constructor === Array; // true
var a = new Array;
a.constructor === Array // true
var n = new Number(3);
n.constructor === Number; // true
那么普通函数创建的实例有没有constructor属性呢?
// 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; // false
p3.constructor === Object; // true
上面代码说明:
(1)普通函数在内部有return操作的就有constructor属性,没有return的没有constructor属性;
(2)有constructor属性的普通函数的constructor属性值不是普通函数本身,是Object。
3、symbol是构造函数吗?MDN 是这样介绍 `Symbol` 的
Symbol是基本数据类型,作为构造函数它不完整,因为不支持语法new Symbol(),如果要生成实例直接使用Symbol()就可以的。
// saucxs
new Symbol(123); // Symbol is not a constructor
Symbol(123); // Symbol(123)
虽然Symbol是基本数据类型,但是Symbol(1234)实例可以获取constructor属性值。
// saucxs
var sym = Symbol(123);
console.log( sym ); // Symbol(123)
console.log( sym.constructor ); // ƒ Symbol() { [native code] }
sym.constructor === Symbol; //true
sym.constructor === Object; //false
这里的constructor属性来自哪里?其实是Symbol原型上的,默认为Symbol函数。
4、constructor的值是只读的吗?回答:如果是引用类型的constructor属性值是可以修改的,如果是基本类型的就是只读的。
引用类型的情况,修改这个很好理解,比如原型链继承的方案中,就是对constructor重新赋值的修正。
// saucxs
function 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也是没有constructor属性的。
// saucxs
function Type() { };
var types = [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)
为什么会这样?因为创建他们的是只读的原生构造函数(native constructors),这个栗子说明依赖一个对象的constructor属性并不安全。
每一个对象都拥有一个原型对象,对象以其原型为模板,从原型集成方法和属性,这些属相和方法都在对象的构造器函数的prototype属性上,而不是对象实例本身上。
上图发现:
1、Parent对象有一个原型对象Parent.prototype,原型对象上有两个属性,分别为:constructor和__proto__,其中__proto__已被弃用。
2、构造函数Parent有一个指向原型的指针constructor;原型Parent.prototype有一个指向构造函数的指针Parent.prototype.constrcutor,其实就是一个循环引用。
上图中可以看到Parent原型(Parent.prototype)上有一个__proto__属性,这是一个访问器属性(即getter函数和setter函数)。作用:通过__proto__可以访问到对象的内部[[Prototype]](一个对象或者null)
`__proto__` 发音 dunder proto,最先被 Firefox使用,后来在 ES6 被列为 Javascript 的标准内建属性。
`[[Prototype]]` 是对象的一个内部属性,外部代码无法直接访问。
// saucxs
function Parent(){};
var p = new Parent();
console.log(p);
console.log(Parent.prototype);
1、p.__proto__获取的是对象的原型,__proto__是每一个实例上都有的属性;
2、prototype是构造函数的属性;3、p.__proto__和Parent.prototype指向同一个对象。
// saucxs
function Parent() {}
var p = new Parent();
p.__proto__ === Parent.prototype
// true
所以构造函数Parent,Parent.prototype和p之间的关系,如下图所示:
以确保 Web 浏览器的兼容性,但是不推荐使用,除了标准化的原因之外还有性能问题。为了更好的支持,推荐使用 `Object.getPrototypeOf()`。
如果要读取或修改对象的 `[[Prototype]]` 属性,建议使用如下方案,但是此时设置对象的 `[[Prototype]]` 依旧是一个缓慢的操作,如果性能是一个问题,就要避免这种操作。
如果要创建一个新对象,同时继承另一个对象的 `[[Prototype]]` ,推荐使用 `Object.create()`。
// saucxs
function Parent() {
age: 50
};
var p = new Parent();
var child = Object.create(p);
这里 `child` 是一个新的空对象,有一个指向对象 p 的指针 `__proto__`。
每一个对象拥有一个原型对象,通过__proto__指针指向上一个原型,并从中继承方法和属性,同时原型对象也可能拥有原型,这样一层层的,最终指向null。这种关系成为原型链(prototype chain),作用:通过原型链一个对象会拥有定义在其他对象中的属性和方法。
// saucxs
function Parent(age) {
this.age = age;
}
var p = new Parent(50);
p.constructor === Parent; // true
p.constructor指向Parent,那么是不是意味着p实例化存在constructor属性呢?并不存在,打印一下p:
有图可以知道,实例化对象p本身没有constructor属性,是通过原型链向上查找__proto__,最终找到constructor属性,该属性指向Parent
// saucxs
function Parent(age) {
this.age = age;
}
var p = new Parent(50);
p; // Parent {age: 50}
p.__proto__ === Parent.prototype; // true
p.__proto__.__proto__ === Object.prototype; // true
p.__proto__.__proto__.__proto__ === null; // true
下图展示原型链运行机制。
1、Symbol是基本数据类型,作为构造函数并不完整,因为不支持语法new Symbol(),但是原型上拥有constructor属性,即Symbol.prototype.constructor。
2、引用类型constructor属性值是可以修改的,但是对于基本类型的是只读的,当然null和undefined没有constructor属性。
3、__proto__是每个实例上都有的属性,prototype是构造函数的属性,这两个不一样,但是p.__proto__和Parent.prototype是指向同一个对象。
4、__proto__属性在ES6时被标准化,但是因为性能问题并不推荐使用,推荐使用Object.getPropertyOf()。
5、每个对象拥有一个原型对象,通过__ptoto_指针指向上一个原型,并从中继承方法和属性,同时原型对象也可能拥有原型,这样一层已成的,最终指向null,这就是原型链。
原文地址:https://www.cnblogs.com/chengxs/p/10862197.html
原型链是一种机制,指的是JavaScript每个对象包括原型对象都有一个内置的[[proto]]属性指向创建它的函数对象的原型对象,即prototype属性。
作用:原型链的存在,主要是为了实现对象的继承。关于原型链的几个概念:1、函数对象在JavaScript中,函数即对象。
2、原型对象当定义一个函数对象的时候,会包含一个预定义的属性,叫prototype,这就属性称之为原型对象。
//函数对象 function F(){}; console.log(F.prototype) |
图片1
3、__proto__JavaScript在创建对象的时候,都会有一个[[proto]]的内置属性,用于指向创建它的函数对象的prototype。原型对象也有[[proto]]属性。因此在不断的指向中,形成了原型链。
举个例子来说,我们将对象F的原型对象修改一下,就可以清楚看到上述的关系
//函数对象 function F(){}; F.prototype = { hello : function(){} }; var f = new F(); console.log(f.__proto__) |
图片2
4、new当使用new去调用构造函数时,相当于执行了
var o = {}; o.__proto__ = F.prototype; F.call(o); |
因此,在原型链的实现上,new起到了很关键的作用。
5、constructor原型对象prototype上都有个预定义的constructor属性,用来引用它的函数对象。这是一种循环引用。
function F(){};F.prototype.constructor === F; |
在实际运用中,经常会有下列的写法
function F(){}; F.prototype = { constructor : F, doSomething : function(){} } |
这里要加constructor是因为重写了原型对象,constructor属性就消失了,需要自己手动补上。
6、原型链的内存结构function F(){ this.name = \'zhang\'; }; var f1 = new F(); var f2 = new F(); |
以上就是js原型链是什么的详细内容,更多请关注网站的其它相关文章!
Demand feedback