JavaScript 的 原型链

首先要明确的是,JavaScript 是面向对象的语言,但与 Java 、 C# 等语言有别,没有类的概念,而是基于原型链 (即使是ES6的"class"也是基于原型链的一种语法糖)。

 

理解原型对象

只要创建一个新函数,系统默认为其创建一个 prototype 属性,指向函数的原型对象。默认情况下,所有原型对象都自动获得一个 constructor 属性,指向prototype 属性所在函数得指针。举个简单例子:

function Person(){
  this.prop = 'myProp'
}
Person.prototype.name = 'zhangsan';
Person.prototype.sayName = function(){
  return this.name
}
var person1 = new Person();    // {prop:myProp}
var person2 = new Person();    // {prop:myProp}
console.log(person1.sayName);      // 'zhangsan'
person1.sayName = 'lisi';          // 修改原型对象上属性
console.log(person1.sayName);      // 'lisi'  证明变量是共享的
console.log(Person.prototype.constructor === Person);    // true
console.log(person1.__proto__ === Person.prototype);     // true

上述例子简单证明了原型链内的一些规则:

  • 使用 "new" 运算符时,实际有几步操作:
    • 创建一个空对象;
    • 为对象添加 "prop" 属性;
    • 为对象的 " [[prototype]] " 属性指向 "Person.prototype"
  • "Person.prototype"为实例对象"person1"和"person2"提供了属性和方法,且实例对象能对原型上的属性方法做修改;
  • "Person.prototype"的"constructor"属性指向原型函数;
  • 实例对象的"[[prototype]]"属性(即__proto__)指向"Person.prototype"。用一个图表示:

 

更简单的原型语法

上述例子中,如果要对"Person.prototype"添加多个属性方法,大可不必一个个列出,因为"prototype"属性指向的就是一个对象,可用如下语法写:

function Person(){}
Person.prototype = {
  name: 'zhangsan',
  age: 24,
  job: 'dazha',
  // ...
}
// 直接赋值方式会导致 prototype 中默认提供的 constructor 属性消失,可显示指定
Person.prototype.constructor = Person

但这种方式设定的"constructor"值的[[Enumrable]]为true,建议使用Object.defineProperty()。

 

判断/获取对象的原型对象

继续以上面例子,有两种方法可以判断对象的原型:

  • Object.getPrototypeOf() 静态方法
var Person_prototype = Object.getPrototypeOf(person1);
console.log(Person.prototype === Person_prototype);        // true
  • instanceof 运算符
console.log(person1 instanceof Person);    // true

 

原型链

关于原型链的定义不多说,大概就是子对象能从父对象读取到属性和方法。先看一个简单例子:

function Parent(){
  this.age = 50;
}
Parent.prototype.name = 'Parent';
Parent.prototype.getAge = function(){
  return this.age
}
function Son(){
  this.age = 24;
}
// 创建匿名对象用于继承
Son.prototype = new Parent()
let son = new Son();
console.log(parent.getAge());        // 24

上述例子中,"son"从"parent"获取"getAge"方法,用于返回自身"age"属性的值,期间大概经历了如下几步:

  • 从自身寻找 "getAge" 方法,未找到则从 "__proto__" 往上找, "__proto__" 指向匿名对象 "new Parent()" ;
  • 从匿名对象中找 "getAge" 方法,未找到则从 "__proto__" 往上找, "__proto__" 指向 "Parent.prototype" ;
  • 从 "Parent.prototype" 找 "getAge" 方法,找到并执行,函数作用域是 "son" 对象,所以返回 "24" 。

上述例子的关系图如下:

如果是"更长"的原型链,是同样的原理,直到最后指向"null"后停止。

 

原型链的简单创造

1. 使用 "Object.create(<proto>, <propertiesObject>?)" 方法可简化上例子,传入参数有两个:(具体用法查看MDN文档)

  • proto:新创建对象的原型对象。
  • propertiesObject:可选。如果没有指定为 undefined,则是要添加到新创建对象的可枚举属性(即其自身定义的属性,而不是其原型链上的枚举属性)对象的属性描述符以及相应的属性名称。
let parent = {
  age: 50,
  getAge() {
    return this.age
  }
}
let son = Object.create(parent, {
  // 属性构造
  age: {
    value: 24
  }
});
console.log(son.age);    // 24

2. 使用 "Object.setPrototypeOf(obj, prototype)" 方法:

  • obj:作为"子"的对象
  • prototype:作为"父"的对象
let obj1 = { a: 1 }
let obj2 = { b: 2 }
Object.setPrototypeOf(obj1, obj2);    // {a:1}
console.log(obj1.b);                  // 2

 

ES6 的 "class" 关键字

ES6的"class"关键字本质是ES5原型链的语法糖,更详细介绍请看 阮一峰的ES6

class Parent {
  // 父类构造函数
  constructor(firstname, lastname) {
    this.firstname = firstname;
    this.lastname = lastname
  }
  say() {
    return 'my name is '+this.lastname + this.firstname
  }
  // 静态方法
  static toString() {
    console.log('Parent static method')
  }
}
// 子类,使用"extents"关键字继承
class Son extends Parent{
  constructor(firstname, lastname, age){
    // 调用"super"方法前无法使用"this"
    super(firstname, lastname)
    this._age = age;
  }
  say(){
    // "super"可获取父类的方法
    let result = super.say()
    return result + '. my age is ' + this._age
  }
  // getter方法,setter同理
  get age(){
    return this._age
  }
  // 同名静态方法,当此方法未定义时,会调用父类的静态方法
  static toString() {
    console.log('Son static method')
  }
}
let parent = new Parent('san', 'zhang');    // "new"创建实例对象
console.log(parent.say());    // 'my name is zhangsan'
Parent.toString();            // 'Parent static method'
let son = new Son('ergou','zhang',16);      // 子类的实例
console.log(son.say());       // 'my name is zhangergou. my age is 16'
Son.toString();               // 'Son static method'
console.log(son.age);         // 16

"class" 有如下特征:

  • "class" 必须使用 "new" 运算符创建实例,直接调用(如 "Parent()" )会报错;
  • "constructor" 构造函数,在创建实例对象时提供属性/方法;
  • 构造函数外部(如 "sayName" )是实例对象的原型方法,即所有继承自"Parent"的实例对象都可以访问的方法;
  • 可以创建 "getter" 和 "setter"
  • 和函数一样,具有 "name" 属性,可以获取类名
  • 和函数一样,可以使用表达式形式定义,如 "const MyClass = class Me {...}" ;
  • 内部可以定义 Generator 方法
  • "static" 定义静态方法,可以被子类继承,该方法不会被实例继承,而是直接通过类来调用;
  • "extends" 实现继承,子类可以获得父类的所有属性和方法;
  • 构造函数内必须调用 "super()" 方法,否则无法使用 "this" 关键字
  • 除了构造函数内,其他函数均可调用 "super" 属性,指向父类
  • ......

 

原型链存在的问题

上例子可以看出,实例对象"person1" 和 "person2" 均能访问原型对象上的属性方法,但是实例对象却能对其修改,导致所有实例对象均产生影响。解决办法是,在实例对象上定义同名属性,则会优先调用实例对象上的属性。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
JavaScript原型链是一种通过原型(prototype)来实现对象之间继承关的机制。每个JavaScript对象都有一个原型对象,用于定义该对象的属性和方法。当访问一个对象的属性或方法时,如果该对象本身没有该属性或方法,JavaScript引擎会沿着原型链向上查找,直到找到该属性或方法为止。实际上,原型链是一个链式结构,每个对象的__proto__属性指向其原型对象,形成了一个继承关系。这样,对象可以继承其原型对象的属性和方法,并且原型对象也可以有自己的原型对象,从而形成更深层次的继承关系。因此,原型链JavaScript中起到了实现对象之间继承关系的重要作用。123 #### 引用[.reference_title] - *1* *3* [JavaScript原型链](https://blog.csdn.net/m0_72446057/article/details/129155515)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}} ] [.reference_item] - *2* [JavaScript原型链(重要)](https://blog.csdn.net/liyuchenii/article/details/125957625)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值