闭包和原型链的继承
>一、闭包
1.闭包的概念
在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用。这样就构成了一个闭包。
function person() {
let name = '张嘉文'
function job() {
console.log(name + '的职业是游戏主播');
}
return job() //此时job是一个闭包函数,他可以访问person这个函数内部声明的变量
}
person() //张嘉文的职业是游戏主播
理解记忆: 把person这个函数理解成一个封闭的包裹,声明的变量就是包裹里的东西,一个闭包就是person调用了一个函数job,返回给了person一个引用了person内部变量的函数,这个返回的函数就叫做闭包
2.闭包的特点
1 使用不当会很容易造成内存泄露(内存中能存的东西越来越少,像是其他部分被泄露了一样)
2 设置私有变量(内部函数调用外部函数的局部变量,此时,这个局部变量就会变成内部函数的私有变量)
3 闭包的作用:充当一个摄像头,函数外部可以访问函数内部的变量,减少变量的声明,避免造成污染.
4 内存的占用比较大,浪费内存.
3.闭包的用途
1).闭包可以让临时变量在外部函数声明周期结束时,仍然存在于内存之中!
/*比如说,如果你希望函数的每次执行结果,都是基于这个函数上次的运行结果*/
var Utils=(function(){
var s=10; // 内部变量
return {
a:function(){
s++;
console.log(s);
}
}
})();
Utils.a(); //11
Utils.a(); //12
2).防止变量污染,设置私有变量
var Utils=(function(){
var s=10; // 内部变量
return {
a:function(){
s++;
console.log(s);
}
}
})();
var s = 100 //全部变量
Utils.a(); //11
Utils.a(); //12
//注意:内部函数调用外部函数的局部变量,此时,这个局部变量就会变成内部函数的私有变量,会一直存在于内部函数中.即使外部函数执行完成后也不会将自己的临时变量删除,因为他发现自己的局部变量在将来可能会被内部函数使用
例题 : 使用闭包函数给每个li绑定事件,点击后立即输出当前li对应的索引,两秒后输出该li的文本内容
<ul>
<li>酸辣土豆丝</li>
<li>水煮肉片</li>
<li>鱼香肉丝</li>
<li>小炒肉</li>
<li>苹果炒香蕉</li>
</ul>
<script>
let lis = document.querySelectorAll('li')
function data() {
var len = lis.length
for (let i = 0; i < len; i++) {
addClick(i)
}
function addClick(j) {
lis[j].onclick = function () {
console.log(j);
let _this = this
setTimeout(function () {
console.log(_this.innerHTML);
}, 2000)
}
}
}
data() //2
// 鱼香肉丝
二、prototype原型和继承
所有函数只要创建出来,系统都会分配一个原型对象给整个函数,通过prototype找到原型对象.原型对象也有原型
我们创建的每个函数都有一个 prototype
(原型)属性。使用原型的好处是可以让所有对象实例共享它所包含的属性和方法。
1.定义:
1).原型是函数的一个属性,通过构造函数产生的实例对象,可以继承该原型上的属性和方法,原型也是对象。
2).利用原型的概念和特点可以提取公共属性和方法
3).实例化对象可通过___ _proto____查看原型
概念讲解
function Person(){
}
Person.prototype.name = '茄子' //原型上的属性
Person.prototype.say = function(){
console.log('这把我带你们赢');
} //原型上的方法
let p1 = new Person()
console.log(p1); //Person {}
console.log(p1.name); // 茄子
p1.say() //这把我带你们赢
提取构造函数中的公共属性
function Person(name,age){
this.major = '土木'
this.city = '成都'
this.name = name
this.age = age
}
//实例化对象
let stu1 = new Person()
let stu2 = new Person()
console.log(stu1) //Person {major: '土木', city: '成都', name: undefined, age: undefined}
console.log(stu2) //Person {major: '土木', city: '成都', name: undefined, age: undefined}
此时可以看出构造函数的多次创建会产生多个相同函数,造成冗余太多。
利用原型prototype解决
Person.prototype ={
major:'土木',
city:'成都'
}
let stu3 = new Person()
let stu4 = new Person()
console.log(stu3) //Person {major: '土木', city: '成都', name: undefined, age: undefined}
console.log(stu4) //Person {major: '土木', city: '成都', name: undefined, age: undefined}
实例化对象用proto访问原型
function Person(name, age) {
this.major = '土木'
this.city = '成都'
this.name = name
this.age = age
}
Person.prototype.grade = 'A'
//构造函数查看原型 prototype
console.log(Person.prototype); //{grade: 'A', constructor: ƒ}
//实例化对象访问原型
let p1 = new Person('陈同学',22)
console.log(p1.__proto__);//{grade: 'A', constructor: ƒ}
原型链继承
JavaScript 中描述了原型链的概念,并将原型链作为实现继承的主要方法。其基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法
构造函数、原型、实例的关系
__proto__是实例对象,拥有的指向原型的属性,而构造函数是没有的
**原型链 😗*绿色的线表示原型链,当zs实例调用一个属性或者方法时,现在自身找,然后一层一层的向上,直至找到null.没有则返回undefined
原型链的改造
Grand.prototype.lastName = 'Ying';
function Grand(){
}
var grondObj = new Grand();
Father.prototype = grondObj;
function Father(){
this.name="zs"
}
var fatherObj = new Father();
Son.prototype = fatherObj;
function Son(){
this.hobbit = 'smoke';
}
var sonObj = new Son();
console.log(sonObj.name); //zs
console.log(sonObj.lastName); //Ying
//当Son要访问一个属性不存在时,就会找到原型的指针__proto__,然后找到原型继续找
call/apply继承
call方法
作用: 调用该函数,并修改函数中this的指向
**语法: ** 函数名. call(对象,实参1,实参2);
function Person(name, age) {
this.name = name;
this.age = age;
console.log(this); //Person {name: 'Ying', age: 18}
}
var personObj = new Person('Ying', 18);
let obj = {}
Person.call(obj, 'Ying', 20);// this =>{name: 'Ying', age: 20}
列题讲解
function Animal(type) {
this.type = type
}
function Cat(name, color) {
this.name = name
this.color = color
}
let A = new Animal('猫科动物')
console.log(A); //Animal {type: '猫科动物'}
let cat1 = new Cat('小花','红色')
console.log(cat1); //Cat {name: '小花', color: '红色'}
思考:怎样让Cat继承Animal的type属性呢
用call实现
function Animal(type) {
this.type = type
}
function Cat(name, color,type) {
this.name = name
this.color = color
Animal.call(this,type)
}
let cat1 = new Cat('小花','红色','猫科动物')
console.log(cat1); //Cat {name: '小花', color: '红色', type: '猫科动物'}
apply 方法
作用: 调用该函数,并修改函数中this的指向
**语法: ** 函数名. apply(对象,数组);
function Animal(type) {
this.type = type
}
function Cat(name, color, type) {
this.name = name
this.color = color
Animal.apply(this, [type])
}
let cat1 = new Cat('小花', '红色', '猫科动物')
console.log(cat1); //Cat {name: '小花', color: '红色', type: '猫科动物'}
区别 : call和apply用法基本一致, 函数名.apply/call(对象, 为call是传入形参//为apply时参数以数组形式参入 )
bind方法
作用: 不调用函数,克隆一个新的函数,并修改新函数中this的指向,将新的函数返回
**语法: ** 函数名. bind(对象,实参);
例 : 在fn中改变this的指向
<ul>
<li>酸辣土豆丝</li>
<li>水煮肉片</li>
<li>鱼香肉丝</li>
<li>小炒肉</li>
<li>苹果炒香蕉</li>
</ul>
<script>
let lis = document.querySelectorAll('li')
function data() {
var len = lis.length
for (let i = 0; i < len; i++) {
addClick(i)
}
function addClick(j) {
lis[j].onclick = function () {
console.log(j);
setTimeout(function () {
console.log(this.innerHTML);
}.bind(this), 2000)
}
}
}
data() // 点击苹果炒香蕉时输出 4 两秒后输出 苹果炒香蕉
</script>
原型继承
function Person(name, age){
this.name = name;
this.age = age;
this.sayHello = function(){
console.log('hello, ' + '我是' + this.name );
}
}
function Student(score){
this.score = score;
}
//这里把Person的实例对象赋值给Student的原型对象,Stu的实例对象就可以使用Person原型上的方法
Student.prototype = new Person();
var stu = new Student(100);
console.log(stu); //{score : 100}
stu.sayHello(); //hello, 我是undefined
//实例化的时候没有传值,所以才会出现这种情况
很明显,方法继承下来了,但是属性却没有继承下来
混合继承
借用构造函数+原型继承
function Person(name, age){
this.name = name;
this.age = age;
this.sayHello = function(){
console.log('hello, ' + '我是' + this.name );
}
}
//父类的原型对象属性
Person.prototype.id = 10;
function Student(name, age, score){
this.score = score;
Person.call(this, name, age); //借用构造函数,继承父类模板
}
Student.prototype = new Person();
var stu = new Student('zs', 18, 100);
console.log(stu.id); //{score : 100, name : zs, age : 18}
stu.sayHello(); //hello, 我是zs
console.log('hello, ' + '我是' + this.name );
}
}
//父类的原型对象属性
Person.prototype.id = 10;
function Student(name, age, score){
this.score = score;
Person.call(this, name, age); //借用构造函数,继承父类模板
}
Student.prototype = new Person();
var stu = new Student(‘zs’, 18, 100);
console.log(stu.id); //{score : 100, name : zs, age : 18}
stu.sayHello(); //hello, 我是zs