Closure&Inherit

1闭包

闭包是这样一种机制: 函数嵌套函数, 内部函数可以引用外部函数的参数和变量,参数和变量不会被垃圾回收机制所收回

这里涉及到几个概念:

1.函数嵌套函数

2.作用域(全局变量和局部变量)变量的访问规则

3.垃圾回收机制(garbage collection):js内部不断扫描内存,并清理无引用对象(自动完成)

    function aa(){

        var num=10;

        function bb(){

            num++

            console.log(num);

        }

        return bb;

    }

    //bb(); //无法直接访问函数内部的函数

 

    aa()();//11

    aa()();//11

    aa()();//11

 

    var closure = aa();

    closure();//11

    closure();//12

    closure();//13

1.1闭包的好处

1.可以让一个变量长期驻扎在内存当中不被释放

2.避免全局变量的污染, 和全局变量不同, 闭包中的变量无法被外部使用

3.私有成员的存在, 无法被外部调用, 只可以自己内部使用

1.2结论(如何描述闭包)

1.闭包是指有权访问另一函数作用域中的变量的函数

2.闭包,可以访问函数内部的局部变量,并让其长期驻留内存

3由于闭包会携带包含它的作用域(运行环境),因此会比其他函数占用更多内存,过度使用闭包可能会造成性能问题。

1.3扩展案例

点击按钮,打印当前索引值

var btn = document.querySelectorAll('button');

方法1let方法

for(let i=0;i<btn.length;i++){

// btn[i].idx = i;

btn[i].onclick = function(){

console.log(i);

// console.log(this.idx);

}

}

 

方法2:闭包法

//下面的闭包中,onclick绑定的匿名函数立即执行,为每一个btn绑定了一个return后的函数,但没有执行,只有onclick后才执行

for(var i=0;i<btn.length;i++){

btn[i].onclick = (function(i){

// 闭包

return function(){

console.log(i);

}

})(i);

}

 

 

 

2 ES5对象扩展

2.1 Object.create(prototype)

以指定的原型创建对象。

2.2Object.defineProperty()

Object.defineProperty(object, propertyname, descriptor),对指定的对象的一个属性设置属性特性。传统方式添加的属性,属性特性都为trueObject.defineProperty添加的属性,属性特性都为false

// 通过Object.defineProperty添加属性,属性特性默认都是false

Object.defineProperty(obj,'hobby',{

value:'很多'

});

 

// 设置age为不可修改的属性

Object.defineProperty(obj, 'age', {

writable:false,

enumerable:false

})

 

2.2.1值属性

1. configurable
可配置性,控制着其描述的属性的修改,表示能否修改属性的特性

2. enumerable
可枚举性,表示能否通过for-in遍历得到属性

3. writable
可写性,表示能否修改属性的值

4. value
数据属性,表示属性的值。默认值为undefined。

2.2.2访问器属性

1. configurable

2. enumerable

3. get
在读取属性时调用的函数。只指定get则表示属性为只读属性。默认值为undefined

4. set
在写入属性时调用的函数。只指定set则表示属性为只写属性。默认值为undefined

2.3 Object.defineProperties()

Object.defineProperties(object, descriptors),对指定的对象的一组属性设置属性特性

2.4 Object.getOwnPropertyDescriptor()

Object.getOwnPropertyDescriptor(object, propertyname),返回属性特性

2.5 Object.keys(object)

Object.keys(object),返回对象所有可枚举属性的名称

2.6 Object.getOwnPropertyNames()

Object.getOwnPropertyNames(object),返回所有属性的名称(哪怕说是不能枚举的属性)。Own的属性,不包括原型对象中的属性。

3原型链

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

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

1.读取实例对象的属性时,先从实例对象本身开始搜索。如果在实例中找到了这个属性,则返回该属性的值;

2.如果没有找到,则继续搜索实例的原型对象,如果在原型对象中找到了这个属性,则返回该属性的值

3.如果还是没找到,则向原型对象的原型对象查找,依此类推,直到Object的原型对象(最顶层对象);

4.如果再Object的原型对象中还搜索不到,则抛出错误

3.1.1重置原型对象

重置原型对象,可以一次性给原型对象添加多个方法,但切断了与原来原型对象的联系。(注意覆盖问题注意识别问题

function Popover(){}

Popover.prototype = {

    show:function(){},

    hide:function(){}

}

3.1.2内置原型对象

使用内置原型可以给已有构造函数添加方法

· 数组/字符串/数字等方法调用原理

· 扩展内置方法

3.2对象属性的遍历与判断

3.2.1 for..in

遍历对象中的所有可枚举属性, 无论该属性存在于实例中还是原型中

3.2.2 in

只要通过对象能够访问到属性就返回true, 无论该属性存在于实例中还是原型中

if(name in s1){

}

3.2.3对象 hasOwnProperty(属性)

检测一个属性是存在于对象本身中

· 返回true,说明属性存在对象中

· 返回false,说明属性不存在或在原型中

检测一个属性是否存在于原型中:!obj.hasOwnProperty(name) && (name in obj)。

4继承

继承是面向对象中一个非常重要的特征。指的是:子类继承父类的属性和方法。我们可以通过继承的方式, 在父类的属性和方法基础上, 让子类也拥有这些属性和方法, 并可以扩展。

4.1继承的好处

1.子类拥有父类所有的属性和方法(代码复用);

2.子类可以扩展自己的属性和方法(更灵活);

3.子类可以重写父类的方法

4.2继承的方式

4.2.1原型链继承

核心:拿父类实例来充当子类原型对象

缺点:1.无法继承构造函数中的属性;2.创建子类实例时,无法向父类构造函数传参;3.原型对象中存在多余的属性

function Student(name,age,gender){

// 属性

this.name = name;

this.age = age;

this.gender = gender;

}

// 原型对象添加方法

Student.prototype.say = function(){

console.log('超能说')

}

Student.prototype.cry = function(){

console.log('超能哭')

}

function Pupil(){

}

// 原型链继承法

Pupil.prototype = new Student();  //利用父类实例充当子类原型对象

Pupil.prototype.constructor = Pupil; //pupil.prototype中的constructor重新设置为pupil,否则puil.prototype将没有constructor属性,无法得知实例由谁创建的

Pupil.prototype.cry = function(){

}

var p1 = new Pupil();

 

4.2.2借用构造函数

核心:借父类的构造函数来增强子类实例,相当于把父类的实例属性复制一份给子类实例

1.call(obj)
格式:父类构造函数.call(子类实例,参数1,参数2,参数3...)

特点:1.立刻执行函数;2.改变函数中的this指向(指向obj)

function Student(name,age,gender){

// 属性

this.name = name;

this.age = age;

this.gender = gender;

}

// 原型对象添加方法

Student.prototype.say = function(){

console.log('超能说')

}

Student.prototype.cry = function(){

console.log('超能哭')

}

function Pupil(name,age,gender){

// 借用构造函数法(继承属性)

Student.call(this,name,age,gender); //this代表的是当前函数中的this,

指向Pupil创建的实例对象

}

var p1 = new Pupil();

2.apply
格式:父类构造函数.apply(子类实例,[参数1,参数2,参数3...])

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

//aplly用法:借用方法

    var arr = [20,2,40,33,21,8,22,46,32]

Math.max.apply(null,arr) //借用了Mathmax方法

 

//call用法

 

//buttons指获取到的页面上的按钮的类数组,但是类数组没有array中的forEach方法,所以通过call借用,funciton(ele,idx){...}Array.forEach(function(ele,idx){..})中的参数

Array.prototype.forEach.call(buttons,function(ele,idx){

//forEach(ele,idx,arr) forEach内可以有三个参数,ele是数组元素,idx为索引值,arr为数组本身

console.log(ele);

ele.onclick = function(){

console.log(idx);

}

});

 

缺点无法实现函数复用函数太多就会影响性能,占用更多内存

4.2.3组合继承

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

继承属性:借用构造函数,只在构造函数中定义属性。

继承方法:原型链继承,把所有的方法写入原型对象。

缺点(原型链继承法的缺点):1.在原型对象中生成多余的属性;2多次执行父类构造函数

4.2.4原型式继承

核心:先创建了一个临时性的构造函数,然后将传入的对象作为这个构造函数的原型,最后返回了这个临时类型的一个新实例解决原型链继承法的缺点:生成多余的属性

Pupil.prototype = object(Student.prototype);

function object(o){

        function F(){}

        F.prototype = o;

        return new F();

    }

ES5版本的原型式继承:Object.create()

Pupil.prototype = Object.create(Student.prototype);

// 解决识别问题

Pupil.prototype.constructor = Pupil;

4.2.5寄生组合继承法

完美的继承方法核心:1.继承属性:借用构造函数;2.继承方法:原型式继承

5 ES6中的继承

5.1 class定义类

ES6提供了更接近传统语言的写法,引入了Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类

//定义类

class Person {

    constructor(name,age) {

        this.name = name;

        this.age = age;

}

// 方法

// 这里添加的方法

// * 自动成为原型对象的方法

// * 自动设置属性特性

    getInfo() {

         return `我叫${this.name},今年${this.age}岁`;;

    }

}

注意:

1.写在类里面的方法实际是给Person.prototype添加方法

2.constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。如果没有constructor方法,则得使用默认的constructor方法

5.2 extends继承

class Person {

    constructor(name, age) {

        this.name = name;

        this.age = age;

    }

}

class Man extends Person {

    constructor(name, age, gender) {

        //this.gender = gender; // 报错

        super(name, age); //生成实例传参时可以少传参数,在super补全即可

        this.gender = gender; // 正确

    }

}            

1.子类继承了父类,在子类构造函数中必须调用super方法。

2.子类的constructor方法没有调用super之前,不能使用this关键字,否则报错,而放在super方法之后就是正确的。

5.3静态方法

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

class Person {

    constructor(){

        this.name = 'laoxie',

        this.age = 18;

    }

    static getInfo(){

        return this.name

    }

    say(){

        console.log(`Hello everyone, my name is ${this.name}, I'm ${this.age} years old`)

    }

}

class Man extends Person {}

1.静态方法方法不会被实例继承,而是直接通过类来调用Person.getInfo()。

2.父类的静态方法,可以被子类继承Man.getInfo()。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值