看代码写结果
var a = {n:4};
var b = a;
b.x = a = {n: 10};
console.log(a.x); // undefined
console.log(b.x); // {n:10}
解析:先开辟一块堆内存,用来存储{n:4}这个对象,a,b指向这个堆内存地址(AAAFFF),执行到第三部得到这个(AAAFFF)地址对应的对象{n:4,x:BBBFFF},a又重新指向新的堆内存地址(BBBFFF),存放着{n:10}
写出你掌握的继承方法
//call继承
function Father(name){
this.name=name
}
function Son(name,age){
Father.call(this,name)
this.age=age
}
//原型继承
const father=Object.create(Father.prototype)
Son.prototype=father
//call与原型继承结合-->寄生组合式继承
//es6的class继承
class Parent{
constructor(name){
this.name=name
}
eat(){
console.log("吃")
}
}
class Son extends Parent{
constructor(name,age){
super(name)
this.age=age
}
}
关于类的继承的总结
在es6之前使用function来定义类的,(既代表方法,又代表类,又代表一个方法实例(对象))
原型继承: 就是子类要继承父类原型上的方法,原理就是让子类的prototype是父类的一个实例,这样子类的实例就可以通过原型链的查找使用父类的方法
Object.create(Father.prototype)得到Father类的的空实例(没有私有属性)
call继承: 就是子类也想有父类同名的私有属性,通过call调用父函数,改变内部的this指向,指向当前的子类的实例,这样就为子类的实例上添加了与父类同名的私有属性
寄生组合式继承: 就是结合call继承与原型继承把父类的私有属性,与prototype上的方法一同继承过来
class继承: 使用extends和super,super相当于call继承,如果在子类的prototype的方法中使用super,super代表父类的prototype(原型对象)
也有一种方式就是让实例的__proto__等于父类的prototype,就可以使用父类方法
call&&apply&&bind的区别
call和apply和bind都是Function原型对象上的方法,所以对于所有的方法实例都可以调用,第一个参数用来改变函数内部this指向,之后的参数就是为调用他们方法传递参数,call传递参数的方式是一个一个传,apply只能传递一个数组,也就是说apply最多传递两个参数,第一个参数就是改变函数内部this指向的,第二个参数是个数组,bind方法并不是让调用着立即执行他会返回一个新的函数,再调用返回的函数时再调用之前那个函数,同时改变内部this指向,如果调用call apply bind时一个参数都不传,内部的this指向window
手动实现call方法
//先处理不传参数的情况,再处理传入的参数是基本数据类型的情况
function myCall(context,...args){
//如果不传参数,那么就改变函数的内部this指向为window
if(context==null){
context=window
}
//如果传入的是基本数据类型,把它转化成引用数据类型
if(!/^(function|object)$/.test(typeof context)){
if(/^(symbol|bigInt)$/.test(typeof context)){
context=Object(context)
}else{
context = new context.constructor(context)
}
}
//现在context一定是引用数据类型
let sym=Symbol("123")
context[sym]=this
let res=context[sym](...args)
delete context[sym]
return res
}
["myCall","myApply","myBind"].forEach(item=>{
Function.prototype[item]=eval(item)
})