企业🤖AI Agent构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
# js面向对象的编程基础 > 今天面试的时候和HR谈了很久,其中问道了面向对象的编程的基础。自己回答的不算太好吧,虽然回答了绝大部分。今天就做个简单的总结吧 ### 简单的继承 ` ~~~ // parentClass function superclass () { this.superValue = 'this is parentClass'; } ~~~ // 添加原型共有方法 superclass.prototype.getsuperVaule = function () { return this.superValue; } // childrenClass function subclass () { this.subclass = 'this is childclass'; } //添加子类原型共有方法 subclass.prototype = new superClass(); //为子类添加构造方法 subclass.prototype.getVaule = function () { return this.subValue; } ` ## 测试使用 ~~~ var instance = new subClass(); console.log(instance.getValue) console.log(instance.getsuperValue) ~~~ ## 解析 其实本质也就是通过将父类的实例赋给子类。之所以将父类实例化,是因为,类要通过prototype才可以访问prototype上面的方法。而对象(也就是类的实例化)可以直接访问(内部是通过__proto__)prototype的方法。子类也是同理了。当我们使用new运算符的时候。对象也就可以访问到prototype上面的方法了。 ## 缺点 1. prototype上面的方法是所以子类公用的。如果是引用类型的变量,那么所有的子类都只有公用一个变量。也就是一个子类改变了引用类型的值,其他子类的引用类型的值也会改变。 2. 无法向父类传递参数,初始化父类。毕竟我们是通过父类的实例化赋值给子类的原型。所以在子类实例化的时候。父类早就定下来了。 ## 构造函数的继承(经典继承) ~~~ function superclass (item) { this.subjects = ['html','css','js']; this.subjects.push(item) } //添加原型共有方法 superclass.prototype.each = function () { this.subjects.forEach( (item,index) => { console.log(item) } } // 声明子类 function subclass (item) { superclass.call(this,item) } ~~~ ## 测试 ~~~ var instance = new subclass('king') console.log(instance.subjects) var instance2 = new subclass('luckyman') console.log(instance2.subjects) instance2.each(); //会报错 ~~~ ## 解析 核心就是通过call或者apply来改变this的指向。将父类的this改变为子类的this。也因为这样子。父类的this上的方法或者属性就会定义到子类当中。但是在父类的原型里的this还是指向父类的。(this指向函数赖以执行的环境,可以认为prototype的执行环境永远都是本身类)。也因此原型上的方法都是无法被继承的。 ## 缺点 1. 无法公用变量或者属性。原型上的方法也无法被继承 ## 组合继承 ~~~ function supclass(item) { //值类型共有变量 this.name = name //引用类型共有属性 this.books = ['html','css','js'] item && this.books.push(item) } //原型方法 superclass.prototype.each() { this.books.forEach( (item,index) => { console.log(item); } } //声明子类 function subclass(item,time) { supclass.call(this,item); this.time = time } //子类原型 subclass.prototype = new supclass(); //子类原型方法 subclass.prototype.getTime = function () { console.log(this.time) } ~~~ ## 测试 ~~~ var instance1 = new subclass('js book',2017'); instance1.each(); instance1.getTime(); var instance2 = new subclass('高级教程', '2014'); instance2.each(); instance2.getTime(); ~~~ ## 解析 组合模式本质就是原型继承和构造函数继承两种方式的组合,继承两种方式的优点 ## 缺点 开销比较大,在子类的原型上,new了一次父类,在构造函数里又执行了new运算符 所以父类执行了两次初始化 ## 原型式继承 ~~~ function inheritobject (0) { //声明过度对象 function F() {} F.prototype = o; //返回过度对象的实例,该实例的原型继承了父类 return new F(); } ~~~ ### 解析 这种因为构造函数基本没有任何的开销,也可以将F过度类缓存起来。这也是Object.create()方法的实现原理。 ### 缺点 1. 引用类型会被所有子类公用 ## 寄生继承 ~~~ function createObj (obj) { // 通过原型继承方式创建新对象 var o = new inheriteobject(obj); // 扩展新对象 o.getName = function () { console.log(name) } // 返回对象 return o; } ~~~ ### 测试 ~~~ var books = { name : 'kingEND', age: 23 } var subbooks = new createObj (books); subooks.getName(); ~~~ ## 最后的继承者 -- 寄生组合式继承 ~~~ function inheritPrototype (subclass, superclass) { // 复制一份父类的原型副本保存在变量中 var p = inheritobject(supclass.prototype); //修正因为重写子类原型导致子类的constructor 属性被修改 // 因为p是父类的prototype,所以constructor会指向父类的构造函数 // 而子类的prototype里的constructor应该是指向子类的构造函数 p.constructor = subclass; // 设置子类的原型 subclass.prototype = p; }