切换导航
简历
工具
演示
查询
导航菜单
服务端
30
前端
114
数据库
82
进阶开发
8
桌面程序
2
QA
15
软件使用
36
规范协议
5
操作系统
11
AI
0
复用资源
6
硬件
0
标签
HTML
正则表达式
Json
包管理工具
vite
CSS
XPath
JavaScript
Jquery
less
ES6
Vue
React
vue 3
Layui
editor.md
sea.js
Zepto.js
mysql.js
mssql.js
NodeJs
Express
前端template
YAML
杂项
外部链接
.NET Core SDK和运行时安装包
淘宝 NPM 镜像
查询网
Tables Generator线上表格转换工具
引用cdn在线脚本和样式
首页
前端
JS 原型(prototype)、隐式原型(__proto__)和原型链(prototype chain)介绍及应用
[TOC] ## 1. 原型 在JavaScript中,每个对象都有一个原型(prototype),它是一个指向另一个对象的引用,也就是说原型是一个对象的实例。 ### 1.1. 函数原型属性 javascript中函数也是一个对象,每个函数中对有一个prototype属性,它是一个实例对象,这个对象主要用于存储特定于该函数构造的对象实例可共享的属性和方法。 - 例如,定义一个函数`Person`,`Person.prototype`可以用来添加所有由`Person`构造出来的对象都能访问的方法,如`Person.prototype.sayHello = function() { console.log('Hello!'); }`。这样,通过`new Person()`创建的对象都可以调用`sayHello`方法。 ```javascript function Person() { } console.log(typeof Person.prototype); // "object" ``` 这个对象(即Person.prototype)的主要用途是作为通过该函数创建的对象(使用new关键字)的原型。当你使用new操作符创建一个对象时,这个新对象会从构造函数(这里是Person)的prototype对象继承属性和方法。 例如,我们可以在Person.prototype上添加一个方法: ```javascript Person.prototype.sayHello = function() { console.log("Hello!"); }; var p = new Person(); p.sayHello(); // "Hello!" ``` 这里,p对象本身没有sayHello方法,但是 JavaScript 会沿着原型链查找,发现p的原型(也就是Person.prototype)上有sayHello方法,于是就调用了这个方法 ### 1.2. 函数属性 在 JavaScript 中,既然函数本身也是对象。那它除了有函数的特性(可以被调用),也有对象的特性,比如有属性。其中prototype就是函数作为对象所拥有的一个属性 ```javascript function myFunction() { } myFunction.someProperty = "value"; console.log(myFunction.someProperty); // "value" ``` 这里我们给myFunction这个函数对象添加了一个属性someProperty,就像给普通对象添加属性一样。 所以,原型是一个复杂的概念,它涉及函数的prototype属性(这个属性的值是一个对象)和对象的__proto__属性(指向其原型对象),它们共同构成了 JavaScript 中的原型链,用于实现继承和属性查找机制 ### 1.3. 对象原型 对象原型:有一个对象实例,这个对象实列中有一个 `__proto__` 属性,每个对象实例都会有 `__proto__` 属性,而这个`__proto__` 属性就是对象的原型,或者可以叫它隐式原型,它指向被实例对象的原型prototype 当你访问一个对象的属性或方法时,如果对象本身没有这个属性或方法,JavaScript 会自动在对象的原型上查找。 例如: ```javascript var obj = {}; console.log(obj.__proto__ === Object.prototype); // true ``` 这里,obj是一个空对象,它的原型(obj.__proto__)指向Object.prototype,这意味着obj可以继承Object.prototype上的方法,如toString、valueOf等 > Tips:在大多数浏览器中可以通过__proto__访问这个属性,不过这是一个非标准的访问方式,标准的方式是使用Object.getPrototypeOf() ## 2. 原型链 原型链是通过对象的`__proto__`属性连接起来的一系列原型对象的链条。 当你访问一个对象的属性或方法时,如果对象本身没有这个属性或方法,JavaScript 引擎会沿着原型链向上查找,直到找到该属性或方法,或者到达原型链的顶端(`Object.prototype.__proto__`为`null`)。 例如: ```javascript function Person() {} Person.prototype.sayHello = function() { console.log('Hello!'); }; var p = new Person(); p.sayName = function() { console.log('My name is unknown.'); }; p.sayHello(); // 通过原型链找到 Person.prototype 上的 sayHello 方法 p.sayName(); // 直接在对象 p 上找到 sayName 方法 ``` 在这个例子中,当调用`p.sayHello()`时,首先在对象`p`上查找`sayHello`方法,找不到后沿着原型链在`p`的原型(即`Person.prototype`)上找到并调用该方法。 总结来说,原型是特定于函数的用于存储共享属性和方法的对象,而原型链是通过对象的`__proto__`属性连接起来的一系列原型对象的链条,用于实现属性和方法的继承与查找。 ## 3. 原型与原型链的基本概念 JavaScript中的原型(prototype)和原型链(prototype chain)时,它们与对象和继承有关。 在JavaScript中,每个对象都有一个原型,它是一个指向另一个对象的引用。这个对象被称为原型对象(prototype object),并且它包含了共享的属性和方法。当你访问一个对象的属性或方法时,如果该对象本身没有这个属性或方法,JavaScript会沿着原型链向上查找,直到找到该属性或方法或者到达原型链的末端(Object.prototype.__proto__为null)。 下面是一个简单的例子来说明原型和原型链的概念: ```javascript // 创建一个对象 var person = { name: "John", age: 30, }; // 访问对象的属性 console.log(person.name); // 输出:"John" console.log(person.age); // 输出:30 // 添加一个新的属性到对象的原型 person.sayHello = function () { console.log("Hello!"); }; // 使用对象的原型方法 person.sayHello(); // 输出:"Hello!" // 创建一个新对象 var student = Object.create(person); student.major = "Computer Science"; // 访问继承的属性 console.log(student.name); // 输出:"John" (继承自原型对象) console.log(student.age); // 输出:30 (继承自原型对象) console.log(student.major); // 输出:"Computer Science" // 修改继承的属性 student.name = "Jane"; console.log(student.name); // 输出:"Jane" (覆盖继承的属性) ``` 下面是对上面示例的详细解释: **一、原型相关内容** 1. `person.sayHello = function () { console.log("Hello!"); };` - 这里为对象`person`添加了一个新的方法`sayHello`,这个方法被添加到了`person`对象的自身属性中。在 JavaScript 中,对象可以直接通过定义属性和方法来扩展自身功能。 **二、原型链相关内容** 1. `var student = Object.create(person);` - 这行代码使用`Object.create`方法创建了一个新对象`student`,并将`person`对象作为其原型。这意味着`student`对象可以通过原型链访问`person`对象上的属性和方法。 - 此时,`student`对象的原型链为:`student` -> `person` -> `Object.prototype` -> `null`。 2. `console.log(student.name); // 输出:"John" (继承自原型对象)`和`console.log(student.age); // 输出:30 (继承自原型对象)` - 当尝试访问`student`对象的`name`和`age`属性时,如果`student`自身没有这些属性,JavaScript 引擎会沿着原型链向上查找,直到找到该属性或者到达原型链的顶端(`null`)。在这个例子中,由于`student`自身没有`name`和`age`属性,所以在其原型`person`对象上找到了这些属性并输出。 3. `student.name = "Jane"; console.log(student.name); // 输出:"Jane" (覆盖继承的属性)` - 这里为`student`对象添加了一个新的属性`name`,并赋值为`"Jane"`。此时,`student`对象自身有了`name`属性,所以在访问`name`属性时,会优先使用自身的属性,而不是从原型上查找。这体现了 JavaScript 中属性查找的优先级,即先在对象自身查找,如果找不到再沿着原型链向上查找。 总之,这段代码通过使用`Object.create`方法创建了一个具有原型链关系的对象,展示了如何通过原型链实现属性的继承和覆盖。 **三、原型链相关内容** 1. 这部分代码中,`sayHello`方法没有添加到`person`对象的`prototype`属性上。 - 它是作为`person`对象的一个自有属性添加的。在JavaScript中,对象有两种类型的属性:自有属性和继承属性。自有属性是直接定义在对象本身上的属性,而继承属性是通过原型链从对象的原型(`__proto__`所指向的对象)中继承而来的属性。 2. 当访问`person.sayHello`时,因为它是`person`对象自身的属性,所以可以直接访问和调用,而不需要通过原型链查找。 3. 对于通过`Object.create(person)`创建的`student`对象来说,`student`对象的原型是`person`。 - 当访问`student.sayHello`时,JavaScript会首先查找`student`对象自身是否有`sayHello`属性。由于`student`对象自身没有这个属性,它会沿着原型链查找,在`student`的原型(也就是`person`)中找到`sayHello`属性并调用。 - 这里虽然`sayHello`不是在`person`的`prototype`属性中定义的,但它仍然可以通过原型链被`student`对象继承和访问,因为`student`的原型就是`person`。 ## 4. 原型链过程指向图  ```javascript let execute = 4 // Object.getPrototypeOf 等价于 __proto__ 都是获取实例对象的隐式原型 if (execute == 0) { // Object 对象原型与隐式原型 console.log(Object.prototype) // 输出 null(原型的尽头就是null) console.log(Object.getPrototypeOf(Object)) // 输出 {}(指向的是 Function.prototype) console.log(Object.__proto__) // 输出 {}(new Function()而来,指向的是 Function.prototype) } else if (execute == 1) { // 实例对象隐式原型 console.log(Object.getPrototypeOf({})) // 输出 null(指向的是 Object.prototype) console.log({}.__proto__) // 输出 null(指向的是 Object.prototype) } else if (execute == 2) { // Function 的原型与隐式原型 console.log(Object.getPrototypeOf(Function)) // 输出 {}(指向的是 Function.prototype) console.log(Function.__proto__) // 输出 {}(new Function()而来,指向的是 Function.prototype) console.log(Function.prototype) // 输出 {}(Function 的原型固定是{}/new Object()) // 结论:Function.prototype原型是{},Object.prototype原型是null,两个是原型的尽头 } else if (execute == 3) { // 普通函数的原型与隐式原型 function Parent() { this.parentProperty = 'parent value'; } Parent.prototype.sayHello = function () { return "hello" } // 在 Parent 函数的原型上添加一个sayHello方法 let a = new Parent(); console.log(Parent.prototype) // 输出 { sayHello: [Function (anonymous)] } console.log(Object.getPrototypeOf(Parent)) // 输出 {} (Parent 是 new Function 而来,隐式原型指向Function的原型) console.log(Object.getPrototypeOf(a)) // 输出 {} (a 是 new Parent 而来,隐式原型指向Parent的原型) } else if (execute == 4) { // 继承一个函数后,子函数的原型和隐式原型 function Parent() { this.parentProperty = 'parent value'; } Parent.prototype.sayHello = function () { return "hello" } // 在 Parent 函数的原型上添加一个sayHello方法 function Child() { this.childProperty = 'child value'; Parent.call(this) } let c = new Child() console.log(c.parentProperty) // 输出:parent value,说明 Child 已经继承了 Parent 的属性 console.log(Child.prototype) // 输出:{},这里并没有输出 { sayHello: [Function (anonymous)] }说明 Child 原型并没有继承自 Parent ,Child 还是 new Function 而来,隐式原型指向Function的原型 console.log(Child.prototype.__proto__) // 输出:null,原型没有继承,因此Child的原型的隐式原型也是指向Objcet的原型,是null // 如果要原型也继承父函数就需要使用下面的方法 Child.prototype = Object.create(Parent.prototype); Child.prototype.constructor = Child; // 这里需要注意,当使用父类的原型给子类创建原型时,创建完成后子类的原型构造函数会是父类(Parent),因此还需将子类的原型构造函数改为自己(Child) Child.prototype.sayChildHello = function () { return "childHello" } // 给Child的原型在添加一个 sayChildHello 函数 let d = new Child() let p = new Parent() console.log(Parent.prototype.__proto__) // 输出:{} console.log(Child.prototype.__proto__ === p.__proto__) // 输出:true console.log(d.sayHello()) // 输出:hello console.log(d.sayChildHello()) // 输出:childHello // 结论:通过上面的示例可知 call 函数只能让对象属性实现继承,并不能让原型继承下去。要实现原型的继承还需要以父类的原型为对象,通过手动的方式给子类创建子类原型 } ```
扫一扫手机看
点击排行
windows 文件资源管理器使用
Layui 弹窗open使用说明
SQL Server 中字符串函数使用
C# 使用QQ邮箱代理做邮件接收功能
SQL Server关于apply的两种形式cross apply 和 outer apply
ASP.NET MVC 模型的自定义验证
nvm安装及常用命令
SQL Server 中数据类型转换
SQL Server 中WITH AS短语的使用
IE浏览器下AJAX缓存问题导致数据不更新的解决办法
订阅号