Typescript之原型对象prototype


文章目录

  • Typescript之原型对象prototype
  • 前言
  • 一、prototype是什么?
    • 1.对象实例的\_\_proto__属性
    • 2.Object.getPrototypeOf()获取对象实例的\_\_proto__属性
    • 3.例子
  • 二、深入了解prototype
    • 1.Function 对象
    • 2.Function.prototype
    • 3.实例
    • 4.prototype 和 Object.getPrototypeOf()的区别
  • 三、原型链的作用
    • 继承
    • 1.属性继承
      • 实例
    • 2.方法继承
      • 实例
  • 四、原型链的性能
  • 总结

前言

本文主要描述在Typescript下的原型对象prototype以及获取原型对象的方法。


提示:以下是本篇文章正文内容,下面案例可供参考

一、prototype是什么?

在JavaScript中,prototype对象是实现面向对象的一个重要机制。每个函数就是一个对象(Function),函数对象都有一个子对象 prototype对象,类是以函数的形式来定义的。prototype表示该函数的原型,也表示一个类的成员的集合。在JavaScript中,当谈到继承时,JavaScript 只有一种结构:对象。在 ES2015/ES6 中引入了 class 关键字,但那只是语法糖,JavaScript 仍然是基于原型的。

1.对象实例的__proto__属性

每个实例对象( object )都有一个私有属性(称之为 __proto__ )指向它的构造函数的原型对象(prototype)。该原型对象也有一个自己的原型对象( __proto__ ) ,层层向上直到一个对象的原型对象为 null。根据定义,null 没有原型,并作为这个原型链中的最后一个环节。

2.Object.getPrototypeOf()获取对象实例的__proto__属性

遵循ECMAScript标准,someObject. __proto__ 符号是用于指向 someObject 的原型对象prototype。从 ECMAScript 6 开始 __proto__ 可以通过 Object.getPrototypeOf() 和Object.setPrototypeOf() 访问器来访问。这个等同于 JavaScript 的非标准但许多浏览器实现的属性 __proto__。但它不应该与构造函数 Function 的 prototype 属性相混淆。被构造函数创建的实例对象的 __proto__ 指向 Function 的 prototype 属性。Object.prototype 属性表示 Object 的原型对象。

3.例子

如下例子说明实例dog的原型链:即
dog–>Animal.prototype – > Object.prototype – > null
实例对象dog的原型对象__proto__就是指向构造函数Animal的prototype属性,而prototype是一个对象,也拥有内部属性__proto_,因此指向构造函数Object的prototype属性。所有属性、方法和行为会层层向上直到一个对象的原型对象为 null为止。

class Animal {name:string;constructor(name:string="Animal") {this.name = name;  }sayHello(){console.log("Hello ",this.name);}
}
console.log(typeof(Animal.prototype)) // objectlet dog = new Animal("Dog")
console.log(Object.getPrototypeOf(dog) === Animal.prototype) // true
console.log(Object.getPrototypeOf(Animal.prototype) === Object.prototype) // true
console.log(Object.getPrototypeOf(Object.prototype) === null) // true

二、深入了解prototype

1.Function 对象

每个被函数声明(function)创建的函数是一个 Function 对象,具有 Function 对象的所有属性、方法和行为。我们查看Function的声明文件,首先它被定义成一个FunctionConstructor的类型的对象类型接口,其中包含2个方法,它们都返回一个叫做Function类型的对象类型接口,和一个只读的prototype的Function对象类型接口的属性。

interface FunctionConstructor {/*** Creates a new function.* @param args A list of arguments the function accepts.*/new(...args: string[]): Function;(...args: string[]): Function;readonly prototype: Function;
}declare var Function: FunctionConstructor;

2.Function.prototype

Function.prototype即FunctionConstructor.prototype,是Function对象类型接口,Function对象类型接口也是一个对象Object.几乎所有 JavaScript 中的对象都是位于原型链顶端的 Object 的实例。每个对象Object都定义了一个构造函数constructor。

interface Object {/** The initial value of Object.prototype.constructor is the standard built-in Object constructor. */constructor: Function;...
}
/*** Creates a new function.*/
interface Function {/*** Calls the function, substituting the specified object for the this value of the function, and the specified array for the arguments of the function.* @param thisArg The object to be used as the this object.* @param argArray A set of arguments to be passed to the function.*/apply(this: Function, thisArg: any, argArray?: any): any;/*** Calls a method of an object, substituting another object for the current object.* @param thisArg The object to be used as the current object.* @param argArray A list of arguments to be passed to the method.*/call(this: Function, thisArg: any, ...argArray: any[]): any;/*** For a given function, creates a bound function that has the same body as the original function.* The this object of the bound function is associated with the specified object, and has the specified initial parameters.* @param thisArg An object to which the this keyword can refer inside the new function.* @param argArray A list of arguments to be passed to the new function.*/bind(this: Function, thisArg: any, ...argArray: any[]): any;/** Returns a string representation of a function. */toString(): string;prototype: any;readonly length: number;// Non-standard extensionsarguments: any;caller: Function;
}

3.实例

如下例子说明function Animal的原型链:即
Animal->Function.prototype – > Object.prototype – > null
实例方法Animal的原型对象__proto__就是指向构造函数Function的prototype属性,而prototype是一个对象,也拥有内部属性__proto_,因此指向构造函数Object的prototype属性。

class Animal {name:string;constructor(name:string="Animal") {this.name = name;  }sayHello(){console.log("Hello ",this.name);}
}console.log(typeof Animal); // function
console.log(Object.getPrototypeOf(Animal) === Function.prototype) // true
console.log(Object.getPrototypeOf(Function.prototype) === Object.prototype) // true
console.log(Object.getPrototypeOf(Object.prototype) === null) // true

4.prototype 和 Object.getPrototypeOf()的区别

你可能已经注意到我们的 function Animal 有一个叫做 prototype 的特殊属性。该特殊属性可与 JavaScript 的 new 操作符一起使用。对原型对象的引用被复制到新实例的内部__proto__属性。例如,当执行 var animal = new Animal(); 时,JavaScript(在内存中创建对象之后,和在运行函数 Animal() 把 this 指向对象之前)设置 animal.__proto__= Animal.prototype;。然后当您访问实例的属性时,JavaScript 首先会检查它们是否直接存在于该对象上,如果不存在,则会__proto__ 中查找。这意味着你在 prototype 中定义的所有内容都可以由所有实例有效地共享,你甚至可以稍后更改部分 prototype,并在所有现有实例中显示更改(如果有必要的话)。

像上面的例子中,如果你执行 var a1 = new Animal(); var a2 = new Animal(); 那么 a1.sayHello() 事实上会指向 Object.getPrototypeOf(a1).sayHello(),它就是你在 Animal.prototype.sayHello 中定义的内容。也就是说:Object.getPrototypeOf(a1).sayHello == Object.getPrototypeOf(a2).sayHello == Animal.prototype.sayHello(补充:实际上,执行 a1.sayHello() 相当于执行 Object.getPrototypeOf(a1).sayHello.call(a1)==Animal.prototype.sayHello.call(a1))

简而言之, prototype 是用于类的,而 Object.getPrototypeOf() 是用于实例的(instances),两者功能一致。

三、原型链的作用

继承

在ES5 的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this))。ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this。

class Parent {}class Child extends Parent {constructor() {super();}
}

注意,super虽然代表了父类Parent的构造函数,但是返回的是子类Child的实例,即super内部的this指的是Child的实例,因此super()在这里相当Parent.prototype.constructor.call(this)。子类Child的构造函数之中的super(),代表调用父类的构造函数。这是必须的,否则 JavaScript 引擎会报错。

原型链图示:

1.属性继承

JavaScript 对象是动态的属性“包”(指其自己的属性)。JavaScript 对象有一个指向一个原型对象的链。当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。

实例

 // 让我们从一个函数里创建一个对象o,它自身拥有属性a和b的:let f = function () {this.a = 1;this.b = 2;}/* 这么写也一样function f() {this.a = 1;this.b = 2;}*/let o = new f(); // {a: 1, b: 2}// 在f函数的原型上定义属性f.prototype.b = 3;f.prototype.c = 4;// 不要在 f 函数的原型上直接定义 f.prototype = {b:3,c:4};这样会直接打破原型链// o.[[Prototype]] 有属性 b 和 c//  (其实就是 o.__proto__ 或者 o.constructor.prototype)// o.[[Prototype]].[[Prototype]] 是 Object.prototype.// 最后o.[[Prototype]].[[Prototype]].[[Prototype]]是null// 这就是原型链的末尾,即 null,// 根据定义,null 就是没有 [[Prototype]]。// 综上,整个原型链如下:// {a:1, b:2} ---> {b:3, c:4} ---> Object.prototype---> nullconsole.log(o.a); // 1// a是o的自身属性吗?是的,该属性的值为 1console.log(o.b); // 2// b是o的自身属性吗?是的,该属性的值为 2// 原型上也有一个'b'属性,但是它不会被访问到。// 这种情况被称为"属性遮蔽 (property shadowing)"console.log(o.c); // 4// c是o的自身属性吗?不是,那看看它的原型上有没有// c是o.[[Prototype]]的属性吗?是的,该属性的值为 4console.log(o.d); // undefined// d 是 o 的自身属性吗?不是,那看看它的原型上有没有// d 是 o.[[Prototype]] 的属性吗?不是,那看看它的原型上有没有// o.[[Prototype]].[[Prototype]] 为 null,停止搜索// 找不到 d 属性,返回 undefined

2.方法继承

JavaScript 并没有其他基于类的语言所定义的“方法”。在 JavaScript 里,任何函数都可以添加到对象上作为对象的属性。函数的继承与其他的属性继承没有差别,包括上面的“属性遮蔽”(这种情况相当于其他语言的方法重写)

实例

 var o = {a: 2,m: function(){return this.a + 1;}};console.log(o.m()); // 3// 当调用 o.m 时,'this' 指向了 o.var p = Object.create(o);// p是一个继承自 o 的对象p.a = 4; // 创建 p 的自身属性 'a'console.log(p.m()); // 5// 调用 p.m 时,'this' 指向了 p// 又因为 p 继承了 o 的 m 函数// 所以,此时的 'this.a' 即 p.a,就是 p 的自身属性 'a'

四、原型链的性能

在原型链上查找属性比较耗时,对性能有副作用,这在性能要求苛刻的情况下很重要。另外,试图访问不存在的属性时会遍历整个原型链。
遍历对象的属性时,原型链上的每个可枚举属性都会被枚举出来。要检查对象是否具有自己定义的属性,而不是其原型链上的某个属性,则必须使用所有对象从 Object.prototype 继承的 hasOwnProperty 方法。
下面给出一个具体的例子来说明它:

 class A {name:stringconstructor(name:sting="defalut"){this.name = name} }let a = new A()console.log(a.hasOwnProperty('name'));// trueconsole.log(a.hasOwnProperty('abc'));// falseconsole.log(a.hasOwnProperty('efg'));// false

注意:检查属性是否为 undefined 是不能够检查其是否存在的。该属性可能已存在,但其值恰好被设置成了 undefined。hasOwnProperty 和Object.keys()是 JavaScript 中唯处理属性并且不会遍历原型链的方法。


总结

文章介绍了构造函数原型对象prototype,以及被它创建的实例对象(instance)的__proto__属性,获取实例__proto__属性的方法Object.getPrototypeOf()。解释了两者的区别以及使用。当实例查找某个属性和方法时候,就根据__proto__属性链条层层向上查询,直到null,但是遍历整个原型链会产生性能问题,使用hasOwnProperty()和Object.keys()判断属性是否在改对象实例中,这样就避免了遍历整个原型链。

Typescript之原型对象prototype深入了解相关推荐

  1. [js高手之路]使用原型对象(prototype)需要注意的地方

    我们先来一个简单的构造函数+原型对象的小程序 1 function CreateObj( uName, uAge ) {2 this.userName = uName;3 this.userAge = ...

  2. 原型对象prototype和原型属性[[Prototype]]

    构造器:可以被 new 运算符调用, Boolean,Number,String,Date,RegExp,Error,Function,Array,Object 都是构造器,他们有各自的实现方式. 比 ...

  3. 原型对象prototype与继承

    原型对象prototype与继承 1.原型对象prototype a) 原型对象是prototype是所有对象的子对象 b) 只能在对象创建后使用 c) 如果向prototype添加属性和方法在对象定 ...

  4. [js高手之路]原型对象(prototype)与原型链相关属性与方法详解

    一,instanceof: instanceof检测左侧的__proto__原型链上,是否存在右侧的prototype原型. 我在之前的两篇文章 [js高手之路]构造函数的基本特性与优缺点 [js高手 ...

  5. javascript详解函数原型对象prototype与constructor

    1.原型模式 首先我们来谈谈prototype属性,也就是原型属性.每当我们创建一个函数时,函数内部都会自动生成一个指针(既自动生成一个属性就是我们说的prototype),这个指针指向指向原型对象, ...

  6. 函数的prototype属性(原型对象)

    1.函数的prototype属性 每个函数都有一个prototype属性,它默认是一个空的Object的实例对象(即称为:原型对象),而原型对象中有一个属性constructor,它指向函数对象.这说 ...

  7. js深入理解构造函数和原型对象

    1.在典型的oop的语言中,如java,都存在类的概念,类就是对象的模板,对象就是类的实例.但在js中不存在类的概念,js不是基于类,而是通过构造函数(constructor)和原型链(prototy ...

  8. JavaScript——面向对象之继承(原型对象)与多态(重载、重写)

    继承与多态 引入问题 一.继承 1. 步骤 (1) 找到所有子对象共同的父对象 (2) 将所有子对象公共的方法定义添加到共同的父对象中 (3) 所有子对象因继承关系而直接使用父对象中公共的方法 2. ...

  9. 细说JavaScript对象(2):原型对象

    JavaScript 并没有类继承模型,而是使用原型对象 prototype 进行原型式继承. 尽管人们经常将此看做是 JavaScript 的一个缺点,然而事实上,原型式继承比传统的类继承模型要更加 ...

最新文章

  1. 三星电子电容器件识别
  2. 扭矩大好还是马力大好_发动机的马力重要还是扭矩重要?加速到底看哪个?
  3. 201128阶段二MVC框架模式、FFmpeg
  4. Android 检查版本更新 Server后台下载
  5. windows查看linux文件中文,Linux 系统下无法查看Windows 中创建的中文文件名
  6. jsp九大内置对象和四种属性范围介绍
  7. jquery 读取页面load get post ajax 四种方式代码写法
  8. VMware Horizon7安装-分步指南
  9. 世界上最漂亮的写作工具Typora(创作者必备软件)
  10. xm list源码分析
  11. 项目管理:实现按时、按预算、基于目标的交付的七个步骤
  12. 基于python的贴吧舆情监控助手实战
  13. AEP(PMM) 傲腾内存特性
  14. weblogic开发EJB
  15. 小米原生浏览器标识.
  16. vs2013下载安装
  17. 计算机管理格式化硬盘,细说电脑怎么格式化硬盘
  18. 5G移动通信技术基本介绍(附92页PDF下载)
  19. 电脑怎么修改html5,详细教你怎么设置电脑默认浏览器
  20. 【关于vue的那些事】vue.runtime.esm.js?2b0e:619

热门文章

  1. 人员定位系统如何赋能智慧电厂转型?
  2. 逆向分析基础 --- 花指令实现及清除
  3. Mobile Inverted Residual Bottleneck Block图解
  4. 摩客怎么设置安卓的dp_简单谈谈Android中SP与DP的区别
  5. 【论文学习】关于联邦学习激励机制的2篇T2论文
  6. 一流城市配电网需要哪些配电技术?了解这些提前布局!
  7. 配电网粒子群算法实例
  8. 谷歌 订阅日历_如何在Google日历中订阅您喜欢的运动队的时间表
  9. 总部在新加坡!招AIGC算法工程师、测试工程师,20-35k,STARY等你来!
  10. python爬虫注意点