您的位置:首页 > 数码常识数码常识

javascript原型和原型链特点(简述JavaScript原型和原型链有什么特点?)

2025-05-10人已围观

javascript原型和原型链特点(简述JavaScript原型和原型链有什么特点?)
  一、前言介绍构造函数,原型,原型链。比如说经常会被问道:symbol是不是构造函数;constructor属性是否只读;prototype、[[Prototype]]和__proto__的区别;什么是原型链?等等问题

  简述JavaScript原型和原型链有什么特点?一、前言

  二、构造函数

  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、constructor属性

  返回创建实例对象的Object构造函数的引用。此属性的值对函数本身的引用,而不是一个包含函数名称的字符串。

  所有对象都会从它的原型上继承一个constructor属性:

  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

  那么普通函数创建的实例有没有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; // falsep3.constructor===Object; // true

  上面代码说明:

  (1)普通函数在内部有return操作的就有constructor属性,没有return的没有constructor属性;

  (2)有constructor属性的普通函数的constructor属性值不是普通函数本身,是Object。

  3、symbol是构造函数吗?

  MDN 是这样介绍 `Symbol` 的

  The `Symbol()` function returns a value of type **symbol**, has static properties that expose several members of built-in objects, has static methods that expose the global symbol registry, and resembles a built-in object class but is incomplete as a constructor because it does not support the syntax "`new Symbol()`".

  Symbol是基本数据类型,作为构造函数它不完整,因为不支持语法new Symbol(),如果要生成实例直接使用Symbol()就可以的。

  // saucxsnew Symbol(123); // Symbol is not a constructorSymbol(123); // Symbol(123)

  虽然Symbol是基本数据类型,但是Symbol(1234)实例可以获取constructor属性值。

  // saucxsvar sym=Symbol(123); console.log( sym ); // Symbol(123)console.log( sym.constructor ); // ? Symbol() { [native code] }sym.constructor===Symbol; //truesym.constructor===Object; //false

  这里的constructor属性来自哪里?其实是Symbol原型上的,默认为Symbol函数。

  4、constructor的值是只读的吗?

  回答:如果是引用类型的constructor属性值是可以修改的,如果是基本类型的就是只读的。

  引用类型的情况,修改这个很好理解,比如原型链继承的方案中,就是对constructor重新赋值的修正。

  // 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也是没有constructor属性的。

  // 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)

  为什么会这样?因为创建他们的是只读的原生构造函数(native constructors),这个栗子说明依赖一个对象的constructor属性并不安全。

  三、原型

  3.1 prototype属性

  每一个对象都拥有一个原型对象,对象以其原型为模板,从原型集成方法和属性,这些属相和方法都在对象的构造器函数的prototype属性上,而不是对象实例本身上。

  上图发现:

  1、Parent对象有一个原型对象Parent.prototype,原型对象上有两个属性,分别为:constructor和__proto__,其中__proto__已被弃用。

  2、构造函数Parent有一个指向原型的指针constructor;原型Parent.prototype有一个指向构造函数的指针Parent.prototype.constrcutor,其实就是一个循环引用。

  3.2 __proto__属性

  上图中可以看到Parent原型(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、prototype是构造函数的属性;

  3、p.__proto__和Parent.prototype指向同一个对象。

  // saucxsfunction Parent() {}var p=new Parent();p.__proto__===Parent.prototype// true

  所以构造函数Parent,Parent.prototype和p之间的关系,如下图所示:

  注意1:`__proto__` 属性在 `ES6` 时才被标准化

  以确保 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。这种关系成为原型链(prototype chain),作用:通过原型链一个对象会拥有定义在其他对象中的属性和方法。

  // saucxsfunction 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

  // 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

  下图展示原型链运行机制。

  五、总结

  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,这就是原型链。

  上面就是小居数码小编今天给大家介绍的关于(简述JavaScript原型和原型链有什么特点?)的全部内容,希望可以帮助到你,想了解更多关于数码知识的问题,欢迎关注我们,并收藏,转发,分享。

  94%的朋友还想知道的:

  软件原型设计工具有哪些类型(软件原型设计工具有哪些)

  axure做app原型图给多大尺寸对照表(axure设计app原型尺寸)

  红外测温仪有什么特点和用途(红外测温仪有什么特点吗)

  产品设计原型图工具(产品经理常用原型设计工具)



  153316
 

很赞哦! ()

随机图文