21、继承+闭包

原型链

实例对象与Object原型对象之间的链条称为原型链

原型模式的访问机制(原型搜索机制)

  1. 读取实例对象的属性时,先从实例对象本身开始搜索。如果在实例中找到了这个属性,则返回该属性的值;
  2. 如果没有找到,则继续搜索实例的原型对象,如果在原型对象中找到了这个属性,则返回该属性的值
  3. 如果还是没找到,则向原型对象的原型对象查找,依此类推,直到Object的原型对象(最顶层对象);
  4. 如果再Object的原型对象中还搜索不到,则抛出错误;

备注:实例对象属性–>其原型对象–>原型对象的原型对象-…>Object的原型对象

继承

继承是面向对象中一个非常重要的特征。指的是:子类继承父类的属性和方法。

我们可以通过继承的方式, 在父类的属性和方法基础上, 让子类也拥有这些属性和方法, 并可以扩展。

一、继承方式

(一).原型链继承:子类的原型对象指向父类的实例对象
缺点:无法直接在实例化子类的同时,给父类的属性赋值
function Animal(jiaosheng){
            this.jiaosheng = jiaosheng;
        }
        Animal.prototype.eat = function(){console.log("吃")}

        function Dog(name,jiaosheng){
            // this:狗的实例对象
            Animal.call(this,jiaosheng);
            this.name = name;
        }
        Dog.prototype = new Animal();
        Dog.prototype.tian = function(){console.log("舔就对了")};
        Dog.prototype.fanzhi = function(){console.log("胎生")};
        var wangcai = new Dog("wangcai","旺");
        //狗的实例wangcai->(Dog.prototype)new Animal()->Animal.prototype->Object.prototype
        console.log(wangcai);
(二).借用构造函数:

将父类的构造函数拿到子类的构造函数中,改变this的执行,同时执行该函数,记得传参。

1、call ==> 改变函数中this的指向,同时执行该函数
格式:父类构造函数.call(子类实例,参数1,参数2,参数3...)
2、apply ==> 改变this的指向,同时执行函数,但是函数的参数要为数组
格式:父类构造函数.apply(子类实例,[参数1,参数2,参数3...])
应用:
1.借用别人的方法

(1.1)借用其他构造函数的原型对象中定义的方法

(1.2)自己本身存在某个方法,但想使用原型链上其他原型对象的某个方法

2.利用apply方法,可以将参数转成数组传入
//aplly用法:借用方法
//类数组要使用map方法
var arr = [20,2,40,33,21,8,22,46,32]
//null是不改变this指向
Math.max.apply(null,arr)

//或者
function Animal(jiaosheng,age){
	this.jiaosheng = jiaosheng;
	this.age = age;
}
Animal.prototype.eat = function(){console.log("吃")}

function Dog(name,jiaosheng,age){
===================================================
// apply方法,参数一定要用数组包含起来。
	Animal.apply(this,[jiaosheng,age]);
	this.name = name;
}
Dog.prototype = new Animal();
Dog.prototype.tian = function(){console.log("舔就对了")};
Dog.prototype.fanzhi = function(){console.log("胎生")};
var wangcai = new Dog("wangcai","旺",2);

区别:call与apply的唯一区别:传参方式不同,call多个参数,apply只有两个参数,第二个参数为数组

bind()/call()/apply()区别

1、bind()改变函数中this的指向,返回新函数

2、call()改变函数中this的指向,同时执行该函数

3、apply()改变this的指向,同时执行函数,但是函数的参数要为数组

优点:解决了子类拷贝了父类的所有属性

缺点:父类存在多余的属性

组合式继承:原型链继承方法+借用构造函数拷贝属性

(三)、组合继承

由于以上继承方法的缺点,实际开发中不可能单纯的只使用一种继承方法,而是利用它们的优点,规避它们的缺点,所以就有了组合继承法

1、继承属性:借用构造函数

​ (1.1)只在构造函数中定义属性

2、继承方法:原型链继承

​ (2.1)把所有的方法写入原型对象

组合继承是最常用的继承模式。

1、缺点(原型链继承法的缺点):

(1.1)在原型对象中生成多余的属性

(1.2)多次执行父类构造函数

//原型链继承基础上补充借用构造函数
function aa(name){
	this.name = name;
}
aa.prototype.eag = function(){
   console.log(18);
}
function bb(home,jj){
   aa.call(this,jj);//==> 让bb继承aa的属性 ==只在构造函数中定义属性
   this.home = home;
}
bb.prototype = new aa();//==> 让bb继承aa的方法 ==把所有的方法写入原型对象
bb.prototype.beijing = function(){
   console.log("北京")
}
bb.prototype.shengzhen = function(){
   console.log("深圳")
}
var hz = new bb("贺州","小明")
console.log(hz)
// bb函数要继承aa函数的属性,需要在bb形参内加上一个形参名,并在函数内加上( 要继承的函数名.call/apply(this,形参名) ==>备注:形参名可随意写,相当于是aa中的实参。

1544613572132

(四)、原型式继承
1、原型式继承

核心:创建空的构造函数F,将F.prototype指向父类的prototype,将子类的prototype指向F的实例。

优点:解决原型链继承法的缺点:生成多余的属性

备注:封装的方法,可直接调用
function object(o){
    function F(){}
    F.prototype = o;
    return new F();
}
子类的原型对象 = object(父类的原型对象);
2.借用构造函数
(2.1)将父类的构造函数拿到子类的构造函数中,改变this的指向为子类的实例,同时执行该函数,记得传参。
(2.2)寄生组合式继承:原型式继承方法+借用构造函数拷贝属性
(五)寄生组合继承法

完美的继承方法

  • 核心:
    • 继承属性:借用构造函数
    • 继承方法:原型式继承

1544613647299

二、es6继承

(一).class定义类

1、写在类里面的方法实际是给Person.prototype添加方法
(1.1) constructor方法是类的默认方法,相当于在构造函数内生成属性

//用class定义
class Animal{
	constructor(jiaosheng,age){
		this.jiaosheng = jiaosheng;
		this.age = age;
	};
	static eat(){
		console.log("吃");
	}
}
//用extends继承父类方法
 class Dog extends Animal{
	constructor(name,js,age){
//super方法(借用构造函数)
		super(js,age);
		this.name = name;
};
//加上static关键字,变为静态方法
	static fanzhi(){
		console.log("胎生");
	};
	tian(){
		console.log("舔狗");
	}
}
var d = new Dog("wangcai","旺",2);
Dog.eat();
Animal.eat();
(二)extends继承
格式:class 子类函数名 extends 父类函数名{}

1、子类继承了父类,在子类构造函数中必须调用super方法(借用构造函数)
2、子类的constructor方法没有调用super之前,不能使用this关键字,否则报错,而放在super方法之后就是正确的。

(三)静态方法

1、如果在一个方法前,加上static关键字,这就称为“静态方法”

static fanzhi(){
	console.log("胎生");
};

(1.1) 静态方法方法不会被实例继承,而是直接通过类来调用Person.getInfo()
(1.2)父类的静态方法,可以被子类继承Man.getInfo()

闭包

1.概念:外函数内部嵌套内函数,同时将内函数返回。内部函数引入外部函数的变量及参数,不会被垃圾回收机制所收回
2.优缺点:
(2.2)可以让一个变量长期驻扎在内存当中不被释放。过度使用闭包,会占用过多的内存,造成性能问题

(2.1)闭包内的变量不会被函数外使用到

案例:有10个按钮
// 点击按钮,打印索引值
// 2.解决方案:
//  * 把var改用let声明
//  * 给每个按钮元素绑定一个属性
for(var i=0;i<btn.length;i++){
	btn[i].idx = i;//把i绑定给idx
	btn[i].onclick = function(){
	console.log(this.idx);//10
	}
}
	*用闭包,用for循环遍历之后,外部函数,用匿名函数,生成直接使用,
	再在内部生成函数,return出去使用。
for(var i=0;i<btn.length;i++){
	btn[i].onclick = (function(idx){
		return function(){
			console.log(idx);
       	}
	})(i);//函数后面加()就是直接使用
}

//备注:如果使用for循环遍历,直接打印,会直接显示10,因为for循环很快,你一点击就直接到10个,而为什么不是9是10,是因为到9时,i还是小于10,还是会加一次,所以会显示10。
for(let i=0;i<btn.length;i++){
	btn[i].onclick = function(){
		console.log(i)
	}
}

1544613735837

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值