多种方式的继承与一道面试题惨案

109 阅读4分钟

继承

继承

  1. 得到了父级的某些功能的使用权限;
  2. 模仿父级的某些功能,并使用这个功能;

克隆式继承

Son.prototype.pay = Father.prototype.pay;(深克隆)
Son.prototype = Father.prototype;(浅克隆)

克隆式继承的(for attr in father.prototype)

for(var attr in Father.prototype){
Son.prototype[attr] = Father.prototype[attr];
}

深克隆

var obj = {
a : {
b : {
c : {
name : 1
}
},
}
}
var clone = {};
var objString = JSON.stringify(obj);
clone = JSON.parse(objString);
这种方法有一个弊端,弊端就是没办法克隆函数
function foo(){
console.log(1);
}
console.log(foo.toString());
var functionString = foo.toString().replace(/^(function)(.+?)(\(\))/,"$1 $3");
let cloneFoo = eval("("+functionString+")");
console.log(cloneFoo);
深克隆函数 利用转换为字符串,在去挑函数名,重点用eval进行解析字符串!

自己封装的一个深克隆的方法

function deepClone(obj){
var clone = {}
for(var attr in obj){
// 如果当前要克隆的属性是对象;
if(typeof obj[attr] === "object"){
// 重新去克隆一份这个对象;
clone[attr] = deepClone(obj[attr])
}else if(typeof obj[attr] === "function" ){
// 函数深克隆;
var functionString = obj[attr].toString().replace(/^(function)(.+?)(\(\))/,"$1 $3");
clone[attr] = eval("("+functionString+")");
}else{
// 基本类型直接使用;
clone[attr] = obj[attr];
}
}
return clone;
}

原型链式继承

1. 实例; => 指针(原型指针) => 当前构造器(构造函数)的原型对象;
2. 原型; => 两个 => 1. constructor (指向当前原型所属的构造函数) ;
                          => 2. __proto__ (原型里面也有原型指针) ; => Object.prototype;
原型链式继承的方法

1.不太兼容方式

Son.prototype.__proto__ = Father.prototype;

2.兼容方式

Son.prototype = new Father();
Son.prototype.constructor = Son;

3.智哥推荐写法(ES5)

Son.prototype = Object.create(Father.prototype);
Son.prototype.constructor = Son;

4.工程化推荐使用(ES6)extends

class Son extends Father{
}

构造器继承

构造函数之中的继承 卧底式继承

重点关注this指向的问题!

function Father(name){
// this => son 的实例化对象;
this.type = "father";
this.name = name;
this.show = function(){
console.log(this.name +":"+"我最后悔的事情就是创建了阿里巴巴");
}
}

卧底的关键改变this指向利用(call,apply)

function Son(name){
// this => 实例化对象;
console.log(this);
// 构造器怎么给实例添加属性 ;
// this.xxx = xxx;
// 构造器的职责其实是代替Son对Son的实例化对象进行加工;
Father.call(this,name);
}

ES6 的构造器继承

class Father{
constructor(name){
this.name = name;
}
}

不改变constructor 或者 constructor 没有参数的情况可以直接继承;

class Son extends Father{
constructor(name,title){
    super(name);
    this.title = title;
}
}
var son = new Son( "Pony" , "用钱制造快乐" );
console.log( son );

如果需要更改 构造函数内的参数(增加或者忽略 ) 在class之中使用了constructor ,那么一定要在开头家加上super

改变不同需求的继承!例如拖拽式继承

首先利用推荐方式的原型链式继承 这个时候在对继承来的原型方法进行重写

Son.prototype = Object.create(Father.prototype);
Son.prototype.constructor = Son;
Son.prototype.move = function(evt){
var e = evt || window.event;
var left = e.clientX - this.offsetX ;
var top = e.clientY - this.offsetY;
top = top < 0 ? 0 : top;
this.ele.style.left = left + "px";
this.ele.style.top = top + "px";
}
var son = new Son();
Son.init("#box3");
寄生式继承-->给原型链继承封装一下

一到面试题引发的惨案

obj.a === 1 && obj.a === 3 && obj.a === 5 && obj.a === 7 让其为true

监听对象属性

首先了解一个方法!

Object.defineProperty(obj, prop, descriptor)

该方法接收三个属性

obj : 对象;
prop : 监听的属性名;
descriptor : 配置参数;


配置的参数有:

1. configurable:当且仅当该属性的 configurable 为 true 时,该属性描述符才能够被改变,同时该属性也能从对应的对象上被删除。默认为 false2. enumerable:当且仅当该属性的enumerable为true时,该属性才能够出现在对象的枚举属性中。默认为 false3. value该属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。默认为 undefined4. writable当且仅当该属性的writable为true时,value才能被赋值运算符改变。默认为 false5. get一个给属性提供 getter 的方法,如果没有 getter 则为 undefined。当访问该属性时,该方法会被执行,方法执行时没有参数传入,但是会传入this对象(由于继承关系,这里的this并不一定是定义该属性的对象)。默认为 undefined6. set一个给属性提供 setter 的方法,如果没有 setter 则为 undefined。当属性值修改时,触发执行该方法。该方法将接受唯一参数,即该属性新的参数值。默认为 undefined

重点是 get 和 set 方法,但注意在设置get和set的时,配置参数就不能设置value和writable,会产生冲突

这个方法可以让我们接触底层封装的Object,修改默认属性

var obj = {};
Object.defineProperty(obj,"a",{
// 是否属性可被删除;
configurable : true,
enumerable : true,
// value : "hahahahhaha",
// 是否可以通过 = 更改value值
// writable : false,
get: function(){
// console.log("property is getting",this);
// this.a === undefined ? this.a = 10 : "";
this.aValue === undefined ? this.aValue = 1 : this.aValue += 2;
return this.aValue;
},
set : function(){
console.log("property is setting");
}
})

obj.a;

console.log(obj.a === 1 && obj.a === 3 && obj.a === 5 && obj.a === 7);

Object.defineProperties()

该方法直接在一个对象上定义新的属性或修改现有属性,并返回该对象。 和上面的方法很像

Object.defineProperties(obj, props)

该方法接收两个参数, 第一:对象 第二:接收可枚举属性相对应的所有属性。(例如)

var obj = {};
Object.defineProperties(obj, {
  'property1': {
    value: true,
    writable: true
  },
  'property2': {
    value: 'Hello',
    writable: false
  }
  // etc. etc.});

此时:对象属性的设置和获取可以被监听了 随意监听事件,并设置获取事件;