js基础之阮一峰的面向对象编程
p
是构造函数P
的实例对象,
但是p
自身没有constructor
属性,该属性其实是读取原型链上面的P.prototype.constructor
属性。
1 2 3 4 5 6 7 8 | Animal.prototype.color = 'white' ; //原型上写公共的方法或属性 //2 如果实例对象自身就有某个属性或方法,它就不会再去原型对象寻找这个属性或方法。 cat1.color = 'black' ; cat1.color // 'black' cat2.color // 'yellow' Animal.prototype.color // 'yellow'; |
一 原型
1.复用:面对对象
面向对象编程是class来实现对象(类-接口)的继承,js则是通过“原型对象”.
1 2 3 4 5 6 7 8 9 | public class Person { int age; public Person( int a){ age=a; } void speak(){ System.out.println( "今年我" + this .age+ "岁" ); } } |
很重要的一个方面,就是对象的继承,这对于代码的复用是非常有用的。
1 2 3 4 5 6 7 | function Person(name){ this .name=name; this .sum= function (){ alert( "this.name" ) } } |
2.构造函数的缺点:无法共享
JavaScript 通过构造函数生成新对象,因此构造函数可以视为对象的模板
构造函数继承的缺点:
二个实例,拥有的方法也变成二个了,浪费系统资源。方法是共享的,应该也共享。
这个问题的解决方法,就是 JavaScript 的原型对象(prototype)。
js中继承机制的思想:是所有的方法和变量都可以共享。定义在原型上就可以共享。
js中函数的原型属性都指向一个对象。
1 2 3 4 5 6 7 8 9 10 | function Dog(name, age) { this .name = name; this .age = age; <strong> this .log = function () { console.log( this .name + ":" + this .age); }</strong> } let dog1 = new Dog( "小黄" , 5); let dog2= new Dog( "小花" ,6); |
对普通的函数没有作用,对构造函数有作用。在外面用上prototype,原型属性和方法。原型对象的属性不是实例对象的属性。
二 原型链
所有对象都有自己的原型链,一个对象可充当的其他对象的原型; 原型对象也是对象,也有自己的原型。因此形成原型链“prototype Chain”
一层层追溯到最顶层就是Object.prototype,所有对象都继承了Object.prototype
的属性。这就是所有对象都有valueOf
和toString
方法的原因
Object.prototype的原型是null,没有任何属性和方法。
“覆盖”:读取对象的某个属性时,JavaScript 引擎先寻找对象本身的属性,如果找不到,就到它的原型去找,如果还是找不到,就到原型的原型去找。如果直到最顶层的Object.prototype
还是找不到,则返回undefined
。
如果对象自身和它的原型,都定义了一个同名属性,那么优先读取对象自身的属性,这叫做“覆盖”(overriding)。
缺点:一级级查找,找不到,会遍历整个原型链,那么很影响属性。
1 2 | typeof f.prototype // "object" Object.getPrototypeOf(Object.prototype) //null |
三 constructor属性
prototype
对象有一个constructor
属性,默认执行原型对象所在的构造函数。因在原型上,那么构造可以被所有对象继承。
1 2 3 4 | function f(){} var f= new f(); f.prototype.constructor===f f.hasOwnProperty(constructor) |
f 是 构造函数f 的 实例对象, f 本身没有construct。
修改原型的属性或方法时,同时也要修改constructor,不然导致这个属性不再指向Person
。由于Person
的新原型是一个普通对象,而普通对象的contructor
属性指向Object
构造函数,导致Person.prototype.constructor
变成了Object
。
下面代码中,要么将constructor
属性重新指向原来的构造函数,要么只在原型对象上添加方法,这样可以保证instanceof
运算符不会失真。
1 2 3 4 5 6 7 8 | C.prototype = { constructor: C, method1: function (...) { ... }, // ... }; // 更好的写法 C.prototype.method1 = function (...) { ... }; |
判断 类型
1 2 3 | v instanceof Vehicle // 等同于 Vehicle.prototype.isPrototypeOf(v) |
四 面试题
1.原型继承
B继承A,先有A与B,(就是先生成function A(){});
缺点:新实例无法向父类构造函数传参。
1 2 | B.prototype= new A(); let newB= new B(); |
2.call借用构造函数构成
在子元素的里面写上
1 | fu.call( this ,name), |
3.组合继承
引用自:https://javascript.ruanyifeng.com/oop/prototype.html#toc1
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 提示词工程——AI应用必不可少的技术
· 字符编码:从基础到乱码解决
· 地球OL攻略 —— 某应届生求职总结