Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

Analysis of JavaScript prototype and inheritance example

2025-01-18 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/01 Report--

今天小编给大家分享一下JavaScript原型与继承实例分析的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。

首先我们需要知道的是,JavaScript 是一种动态语言,本质上说它是没有Class(类)的;但是它也需要一种继承的方式, 那就是原型继承;JavaScript 对象的一些属性和方法都是继承自别的对象。

很多同学对 JavaScript 的原型和继承不是很理解,一个重要的原因就是大家没有理解__proto__和prototype这两个属性的意思。 接下来我们先来好好梳理一下这两个属性,看看它们存在哪里,代表了什么意义,又有什么作用。

首先来说一下__proto__这个属性吧,我们需要知道的是,除了null和undefined,JavaScript中的所有数据类型都有这个属性; 它表示的意义是:当我们访问一个对象的某个属性的时候,如果这个对象自身不存在这个属性, 那么就从这个对象的__proto__(为了方便下面描述,这里暂且把这个属性称作p0)属性上面 继续查找这个属性,如果p0上面还存在__proto__(p1)属性的话,那么就会继续在p1上面查找响应的属性, 直到查找到这个属性,或者没有__proto__属性为止。

我们把一个对象的__proto__属性所指向的对象,叫做这个对象的原型;我们可以修改一个对象的原型来让这个对象拥有某种属性,或者某个方法。

// 修改一个Number类型的值的原型const num = 1;num.__proto__.name = "My name is 1";console.log(num.name); // My name is 1// 修改一个对象的原型const obj = {};obj.__proto__.name = "dreamapple";console.log(obj.name); // dreamapple

这里需要特别注意的是,__proto__这个属性虽然被大多数的浏览器支持,但是其实它仅在ECMAScript 2015 规范中被准确的定义, 目的是为了给这个传统的功能定制一个标准,以确保浏览器之间的兼容性。通过使用__proto__属性来修改一个对象的原型是非常慢且影响性能的一种操作。 所以,现在如果我们想要获取一个对象的原型,推荐使用Object.getPrototypeOf 或者Reflect.getPrototypeOf,设置一个对象的原型推荐使用Object.setPrototypeOf或者是Reflect.setPrototypeOf。

到这里为止,我们来对__proto__属性做一个总结:

存在哪里? 除了null和undefined所有其他的JavaScript对象或者原始类型都有这个属性

代表了什么? 表示了一个对象的原型

有什么作用? 可以获取和修改一个对象的原型

说完__proto__属性,接下来我们就要好好的来理解一下prototype属性了;首先我们需要记住的是,这个属性一般只存在于函数对象上面; 只要是能够作为构造器的函数,他们都包含这个属性。也就是说,只要这个函数能够通过使用new操作符来生成一个新的对象, 那么这个函数肯定具有prototype属性。因为我们自定义的函数都可以通过new操作符生成一个对象,所以我们自定义的函数都有prototype 这个属性。

// 函数字面量console.log((function(){}).prototype); // {constructor: ƒ}// Date构造器console.log(Date.prototype); // {constructor: ƒ, toString: ƒ, toDateString: ƒ, toTimeString: ƒ, toISOString: ƒ, …}// Math.abs 不是构造器,不能通过new操作符生成一个新的对象,所以不含有prototype属性console.log(Math.abs.prototype); // undefined

那这个prototype属性有什么作用呢?这个prototype属性的作用就是:函数通过使用new操作符生成的一个对象, 这个对象的原型(也就是__proto__)指向该函数的prototype属性。 那么一个比较简洁的表示__proto__和prototype 属性之间关系的等式也就出来了,如下所示:

// 其中F表示一个自定义的函数或者是含有prototype属性的内置函数new F().__proto__ === F.prototype // true

看到上面等式,我想大家对于__proto__和prototype之间关系的理解应该会更深一层了。

好,接下来我们对prototype属性也做一个总结:

存在哪里? 自定义的函数,或者能够通过new操作符生成一个对象的内置函数

代表了什么? 它表示了某个函数通过new操作符生成的对象的原型

有什么作用? 可以让一个函数通过new操作符生成的许多对象共享一些方法和属性

其实到这里为止,关于JavaScript的原型和继承已经讲得差不多了;下面的内容是一些基于上面的一些拓展, 可以让你更好地理解我们上面所说的。

当我们理解了上面的知识点之后,我们就可以对下面的表达式做一个判断了:

// 因为Object是一个函数,函数的构造器都是FunctionObject.__proto__ === Function.prototype // true// 通过函数字面量定义的函数的__proto__属性都指向Function.prototype(function(){}).__proto__ === Function.prototype // true// 通过对象字面量定义的对象的__proto__属性都是指向Object.prototype({}).__proto__ === Object.prototype // true// Object函数的原型的__proto__属性指向nullObject.prototype.__proto__ === null // true// 因为Function本身也是一个函数,所以Function函数的__proto__属性指向它自身的prototypeFunction.__proto__ === Function.prototype // true// 因为Function的prototype是一个对象,所以Function.prototype的__proto__属性指向Object.prototypeFunction.prototype.__proto__ === Object.prototype // true

如果你能够把上面的表达式都梳理清楚的话,那么说明你对这部分知识掌握的还是不错的。

谈及JavaScript的原型和继承,那么我们还需要知道另一个概念;那就是constructor,那什么是constructor呢? constructor表示一个对象的构造函数,除了null和undefined以外,JavaScript中的所有数据类型都有这个属性; 我们可以通过下面的代码来验证一下:

null.constructor // Uncaught TypeError: Cannot read property 'constructor' of null ...undefined.constructor // Uncaught TypeError: Cannot read property 'constructor' of undefined ...(true).constructor // ƒ Boolean() { [native code] }(1).constructor // ƒ Number() { [native code] }"hello".constructor // ƒ String() { [native code] }

但是其实上面这张图的表示并不算准确,因为一个对象的constructor属性确切地说并不是存在这个对象上面的; 而是存在这个对象的原型上面的(如果是多级继承需要手动修改原型的constructor属性,见文章末尾的代码),我们可以使用下面的代码来解释一下:

const F = function() {};// 当我们定义一个函数的时候,这个函数的prototype属性上面的constructor属性指向自己本身F.prototype.constructor === F; // true

关于constructor还有一些需要注意的问题,对与JavaScript的原始类型来说,它们的constructor属性是只读的,不可以修改。 我们可以通过下面的代码来验证一下:

(1).constructor = "something";console.log((1).constructor); // 输出 ƒ Number() { [native code] }

当然,如果你真的想更改这些原始类型的constructor属性的话,也不是不可以,你可以通过下面的方式来进行修改:

Number.prototype.constructor = "number constructor";(1).constructor = 1;console.log((1).constructor); // 输出 number constructor

当然上面的方式我们是不推荐你在真实的开发中去使用的,接下来,我会使用一些代码来把今天讲解的知识再大致的回顾一下:

function Animal(name) { this.name = name;}Animal.prototype.setName = function(name) { this.name = name;};Animal.prototype.getName = function(name) { return this.name;};function Dog(name, breed) { Animal.call(this, name); this.breed = breed;}Dog.prototype = Object.create(Animal.prototype);// 因为上面的语句将我们原来的prototype的指向修改了,所以我们要重新定义Dog的prototype属性的constructor属性Reflect.defineProperty(Dog.prototype, "constructor", { value: Dog, enumerable: false, // 不可枚举 writable: true});const animal = new Animal("potato");console.log(animal.__proto__ === Animal.prototype); // trueconsole.log(animal.constructor === Animal); // trueconsole.log(animal.name); // potatoconst dog = new Dog("potato", "labrador");console.log(dog.name); // potatoconsole.log(dog.breed); // labradorconsole.log(dog.__proto__ === Dog.prototype); // trueconsole.log(dog.constructor === Dog); // true以上就是"JavaScript原型与继承实例分析"这篇文章的所有内容,感谢各位的阅读!相信大家阅读完这篇文章都有很大的收获,小编每天都会为大家更新不同的知识,如果还想学习更多的知识,请关注行业资讯频道。

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report