1.闭包
问题演示:闭包的作用
控制台是否有输出
<script>
function foo(){
var a = 10;
function bar(){
a++;
console.log(a);
}
bar();
}
foo();//11
foo();//11
</script>
//闭包:在一个函数外部能够访问函数内部局部变量的函数,我们把这个函数称为闭包(函数),把变量称为闭包变量
//作用域链 : 逐层向上级作用域查找时形成一个链条
function foo(){
var a =10;
function bar(){
a++;
console.log(a);
}
return bar;
}
// foo();//没有输出
var baz = foo();
console.log(baz);//得到函数
baz();//11
baz();//12
1.2闭包的经典应用
点击对应的li输出相应索引
<body>
<ul>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
<script>
//请使用闭包实现 点击li输出对应的索引
let aList = document.querySelectorAll("li");
for(let i =0;i<aList.length;i++){
aList[i].onclick =()=>{
console.log(i);
};
}
</script>
</body>
使用var变量
for(var i=0;i<aList.length;i++){
aList[i].onclick = function(){
console.log(i);
//i是全局变量输出全是5
};
}
使用闭包实现
for(var i=0;i<aList.length;i++){
//立即执行的匿名函数
aList[i].onclick = (function(i){
return function(){
console.log(i);//这个i是个局部变量,访问的是外层函数的形参i
};
})(i);
}
1.3闭包的好处
//减少全局变量的定义,避免污染环境,避免冲突
var a=10;
let b=20;
var a=30;
let b =30;//报错
//使用匿名函数立即执行
let obj = (function(){
var a = 10;
let b = 20;
return{
fn1(){
a++;
console.log(a);
},
fn2(){
b++;
console.log(b);
}
}
})();
obj.fn1();//11
obj.fn1();//12
1.4闭包的缺点和好处
//内存泄露:一块内存空间既不能被使用也不能释放(回收)
//垃圾回收机制 引用计数 标记清除
//内存泄露:一块内存空间既不能被使用也不能释放(回收)
//垃圾回收机制 引用计数 标记清除
let obj1 = {a:1,b:2};//引用次数为1,不能被清除
obj =null;//引用次数为0
//形成循环清不掉
let obj2 =obj1;
obj1.prop1 =obj2;
obj2.prop2 =obj1;
1.5闭包面试题
//第一道
function fun(n,o){
console.log(o);//对应的是第二个实参的值,如果没有传第二个实参,undefined;
//返回一个对象 对象里有一个方法
return{
fun:function(m){
return fun(m,n);//fun对应全局函数fun,m对应的fn方法里的形参吗, n对应的是全局函数fun里的形参
},
};
}
var a =fun(0);// undefined a对应的fun这个函数的返回值,是一个对象,这个对象里有一个方法fun
a.fun(1);//fun(1,0) 0
a.fun(2);//fun(2,0) 0
a.fun(3);//fun(3,0) 0
//第二道
var b = fun(0).fun(1).fun(2).fun(3)//undefined 0 1 2
var c= fun(0).fun(1);
c.fun(2);//undefined 0
c.fun(3);//1 1
2.继承
2.1继承解释
//继承:一个类的实例能够访问另外一个类里定义的属性和方法
//子类和父类的说法
function Parents(){
}
function Children(name,age){
this.name = name;
this.age = age;
this.sayHello = function(){};
}
let xixi = new Children("xixi",20);
2.2call或apply继承
function Person(name, age) {
this.name = name;
this.age = age;
this.sayHello = function () {
console.log(this.name);
};
}
function Male(name, age) {
Person.call(this, name, age); //把person里面的代码拿过来执行,并且改变this指向,让其指向Male的实例,相当于指定在Male里写this.name = name……
this.gendor = "男";
}
function Female(name, age) {
Person.apply(this, [name, age]);
this.gendor = "女";
}
let ych = new Male("yinchuangjie", 20);
ych.sayHello();
let sss = new Female("shishanshan", 20);
sss.sayHello();
2.3原型继承
//原型对象:每一个函数内部有个属性叫prototype,prototype里的属性和方法能被实例所访问,我们把这个属性称为原型(原型对象)
//每一个实例对象里有一个内部属性__proto__,这个属性指的是prototype
function Person(){}
console.log(Person.prototype);
console.log(Person.prototype.constructor === Person);//true
let p = new Person();
console.log(p.__proto__ === Person.prototype);//true
//如何实现原型继承
//如果让一个类型的原型对象等于另外一个类型的实例,就可以实现继承
function Person(){}
Person.prototype.name = "zhangmeng";
Person.prototype.age = 20;
Person.prototype.sayHello = function(){
console.log(this.name);
}
//可以让Male的原型对象等于Person的实例
function Male(){}
Male.prototype = new Person();//实现了原型继承 为什么?
let male = new Male();
male.sayHello();
console.log(Male.prototype.__proto__ === Person.prototype);//ture
console.log(male.__proto__ === Male.prototype);//true
//访问一个实例的属性和方法有一个查找顺序
//首先,看当前实例自身有没有这些属性和方法,如果自身有,直接访问
//其次,看当前实例__proto__指向的原型对象上的__proto__对应的那个原型对象上有没有如果有的话,直接访问
//以此类推,最终会查找到Object.prototype,如果有,直接访问,没有的话,得到undefined
//沿着__proto__逐层查找形成的一个链式结构,称为原型链
console.log(male.__proto__ === Male.prototype);//true
console.log(Male.prototype);
console.log(Male.prototype.__proto__ === Person.prototype);//ture
console.log(male.__proto__.__proto__ === Peoson.prototype);//true
//原型链
//自身有属性访问自身(C的实例属性)
如果没有取原型上找 (C的一个原型属性)
function A() {}
A.prototype.prop = "A的一个原型属性";
function B() {}
B.prototype.prop = "B的一个原型属性";
function C() {
this.prop = "C的实例属性"
}
C.prototype.prop = "C的一个原型属性";
let c = new C();
console.log(c.prop);
//先C的实例,没有C的原型属性
//继承后 C实->C原 -> B -> A
function A() {}
A.prototype.prop = "A的一个原型属性";
function B() {}
// B.prototype.prop = "B的一个原型属性";
function C() {
// this.prop = "C的实例属性"
}
// C.prototype.prop = "C的一个原型属性";
B.prototype = new A();
C.prototype = new B();
let c = new C();
console.log(c.prop);
function A() {}
A.prototype.prop = "A的一个原型属性";
function B() {
this.prop = "B的一个实例属性"
}
function C() {
}
C.prototype = new B();
console.log(C.prototype);
let c = new C();
console.log(c.prop);
//原型链顶端
function A() {}
A.prototype.prop = "A的一个原型属性";
function B() {}
// B.prototype.prop = "B的一个原型属性";
function C() {
this.prop = "C的实例属性"
}
C.prototype.prop = "C的一个原型属性";
B.prototype = new A();//B继承了A
C.prototype = new B();//C继承了B
let c = new C();
console.log(c.__proto__ === C.prototype);//true
console.log(c.__proto__.__proto__ === B.prototype);//true
console.log(c.__proto__.__proto__.__proto__ === A.prototype);//true
console.log(c.__proto__.__proto__.__proto__.__proto__ ===Object.prototype);
console.log(Object.prototype,Object.prototype.__proto__);//Object.prototype是任意原型链的顶端
2.4组合继承
通过call方法实现属性继承,通过原型继承和循环实现方法继承
//组合创建
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.sayHello = function () {
console.log(this.name);
};
function Male(name, age) {
Person.call(this, name, age); //已经实现了属性的继承
}
//只需要考虑原型方法的继承
//Male.prototype = Person.prototype; //这种写法有一个问题,地址指向一致
//目的 是把父类原型对象上的方法给到子类的原型对象,并且地址还不能一样
for (let i in Person.prototype) {
Male.prototype[i] = Person.prototype[i];
}
Male.prototype.sayHi = function () {
console.log("hi");
};
let male = new Male("彭震", 20);
male.sayHello();
let person = new Person("高真爱", 20); //父类访问子类里的方法,有逻辑上错误
person.sayHi();
2.5寄生式组合创建
<script>
//Object里面提供的一种新的方法
//let obj1 = Object.create(obj); 用来创建一个对象,创建出来的对象有一个特点 obj1.__proto__== obj
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.sayHello = function () {
console.log(this.name);
};
function Male(name, age) {
Person.call(this, name, age); //已经实现了属性的继承
}
//实现原型的继承
Male.prototype = Object.create(Person.prototype);
//修正
Male.prototype.constructor = Male;
//console.log(Male.prototype.__proto__ === Person.prototype);
let male = new Male("王晓林", 20);
male.sayHello();
//反向查找构造函数 通过实例找到对应的构造函数
console.log(male.__proto__ === Male.prototype);
console.log(male.__proto__.constructor);
2.6extends继承
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
sayHello() {
console.log(this.name);
}
}
//class Male extends Person {} //继承实现
//需求 子类里有自己的属性和方法
class Male extends Person {
constructor(name, age) {
//super 创建this并改变this指向,执行父类构造函数里的代码
super(name, age);
this.gender = "男";
}
sayHi() {
console.log("hi " + this.name);
}
}
let male = new Male("wangxiaolin", 20);
male.sayHello();
male.sayHi();
2.7面试题
1.每一个函数都有一个原型对象(prototype)(true)
2.每一个构造函数都有一个原型对象(prototype)(true)
3.每一个对象都有一个原型对象(prototype)(false)
4.每一个函数都有一个内部属性__proto__(ture)
5.每一个构造函数都有一个内部属性__proto__(ture)
6.每一个对象都有一个内部属性(true)
function foo(){
console.log(this);
//写在这实例可以访问到
// this.getName = function(){
// console.log(1);
// }
}
//添加一个属性 new 跟实例没有半毛钱关系
foo.getName = function(){
console.log(2);
};
//给prototye.getName添加一个属性
foo.prototype.getName = function(){
console.log(3);
}
//实例 访问这个方法首先在构造函数上找 没有找原型对象
new foo().getName();//3
//需求 : 给任意数组添加两个方法 getMin() getMax() ,当数组调用这两个方法时,能得到数组中的最小值和最大值
let arr = [11, 22, 10, 77];
//console.log(arr.__proto__);
Array.prototype.getMin = function () {
return Math.min.apply(null, this);
};
Array.prototype.getMax = function () {
return Math.max.apply(null, this);
};
console.log(arr.getMin(), arr.getMax());