JavaScript中的继承
JS中的继承是我们工作中提高工作效率的非常实用且高效的一种手段,也是求职面试比问的一个知识模块。
常见的几种继承方式
- 原型式继承
- 构造函数式继承
- 拷贝式继承
- Class继承
- hasOwnProperty():检测属性(不会)
原型式继承
原型式继承指的是:将父类的实例作为子类的原型。
// 原型式继承指的是:将父类的实例作为子类的原型。
// 父类(超类/基类)
function Person(name){
this.name = name || "川振华"
this.sayHello = function(){
console.log(`我是${this.name},我是懂王`);
}
}
Person.prototype.tp = function(){
console.log(`我振华已经到太阳上了`);
}
// 子类
function Student(){}
Student.prototype = new Person()
// 对子类的原型的构造器重新定义
Student.prototype.constructor = Student
let stu = new Student()
console.log( stu.constructor )
console.log( stu.name )
stu.sayHello()
stu.tp()
构造函数式继承
构造函数式继承主要是利用了call()和apply()两个方法。
JavaScript中的每一个Function对象都有一个apply()方法和一个call()方法。
-
apply():调用一个对象的一个方法,用另一个对象替换当前对象。例如:B.apply(A, arguments);即A对象应用B对象的方法。
-
call():调用一个对象的一个方法,用另一个对象替换当前对象。例如:B.call(A, args1,args2);即A对象调用B对象的方法。
let fn = new Function("haha") // Function.prototype.call = function(){} // Function.prototype.apply = function(){}
/*apply()方法*/ // fn.apply(thisObj[, argArray]) /*call()方法*/ // fn.call(thisObj[, arg1[, arg2[, [,...argN]]]]); function getSum( n1,n2 ){ return n1+n2 } function getDiff( n1,n2 ){ return n1-n2 } // getDiff调用getSum的功能 let result1 = getSum.call(getDiff,10,5) console.log( result1 ); let result2 = getSum.apply(getDiff,[10,5]) console.log(result2); // 父类 function Person(name){ this.name = name || "川振华" this.sayHello = function(){ console.log(`我是${this.name},我是懂王`); } } Person.prototype.tp = function(){ console.log(`我振华已经到太阳上了`); } // 子类 function Student(name){ Person.call(this,name) } let stu = new Student("建国") console.log(stu); // stu.tp() // 面试题:找出数组中的最大数字 // let a = Math.max(10,20,3,45,21,14,55,22,74,56,9,58) let arr = [10,20,3,45,21,14,55,22,74,56,9,58] let a = Math.max.apply(null,arr) console.log(a);
call()方法和apply()方法的区别
这两个方法都是用另一个对象替换当前的对象。
这两个方法的不同点主要表现在参数上。
- call():可以有无限制个参数
- 参数1:新的this对象
- 参数2,3,4...:其他参数
- apply():只能有两个参数
- 参数1:新的this对象
- 参数2:数组或类数组对象
拷贝式继承
拷贝式继承也是我们常用的一种手段,但是拷贝也有区分:
- 浅拷贝:直接赋值拷贝。
- 深拷贝:将A对象的属性全部复制到B对象上。
浅拷贝
let obj1 = {
name:"tom",
age:20,
hobby:["drink","smoke","basketball","sing","dance","rap"]
}
let obj2 = obj1
obj2.name = "jack"
console.log(obj1);
console.log(obj2);
所谓的浅拷贝就类似于数组的赋值,而实际上就是把obj1的地址值传给了obj2。那么地址一样的话这两个对象的值也会相互影响。
深拷贝
// 深拷贝
let obj3 = {}
for( let key in obj1 ){
obj3[key] = obj1[key]
}
obj3.name = "jack"
console.log(obj1);
console.log(obj3);
深拷贝的好处在于对obj2的操作不会影响到obj1对象,但是上面的代码其实是有bug的。
// 深拷贝
let obj3 = {}
for( let key in obj1 ){
obj3[key] = obj1[key]
}
obj3.name = "jack"
obj3.hobby.push("program")
console.log(obj1);
console.log(obj3);
当对象obj1的属性的值不再是基本数据类型时,我们就不能直接简单的复制了,因为简单的复制还是直接搬运别人的地址值。 所以我们需要更加严谨一些,我们可以使用递归。
解法一
// 解法1
function deepClone( obj ){
let newObj = new obj.constructor()
// if( Array.isArray(obj) ){ // new obj.constructor() => new Student()
// newObj = []
// }else{
// newObj = {}
// }
for( let key in obj ){
// hobby
if( typeof obj[key] === "object" && obj[key] !== null ){
newObj[key] = deepClone( obj[key] )
}else{
newObj[key] = obj[key]// name age
}
}
return newObj
}
let o = deepClone(obj1)
// o.hobby.push("aahjhbja")
// console.log(obj1);
// console.log(o);
解法二
//解法二
let obj4 = JSON.parse( JSON.stringify(obj1) )
obj4.hobby.push("haha")
console.log(obj1);
console.log(obj4);
Class继承
Class继承则需要使用super() 和extends两个关键字。
// 定义父类
class Fu{
// 构造器
constructor(name){
this.name = name
}
// Fu.prototype.show
show(){
console.log(`我是${this.name}`);
}
}
// 定义子类继承父类
class Zi extends Fu{
constructor(name,age){
// 子类继承父类时,要求需要在构造器中调用父类的构造器
super(name)
this.age = age
}
}
let z = new Zi("tom",20)
console.log(z);
hasOwnProperty():检测属性
Object的hasOwnProperty()方法返回一个布尔值,判断对象是否包含特定的自身(非继承)属性。
var o ={x:1}
o.hasOwnProperty("x"); true
o.hasOwnProperty("y"); false //非继承返回true
o.hasOwnProperty("toString"); false //继承属性返回false