一、什么是闭包?
//1、for循环之中: // for循环之中的i变量会因为for的循环次数被覆盖,所以在for循环内部存在函数时,而且这个函数内会调用i变量,这种情况下就需要用到闭包。 for (var i = 0; i < 10; i++) { console.log(i); //可以访问到每次的i } // 必须满足两个条件: // 1.在for循环内存在函数 // 2.函数内会调用这个变量 var ali = document.getElementsByTagName("li"); for(var i=0;i<10;i++){ ali[i].onclick = function(){ console.log(i); //在函数内部就无法访问到外部变量 } } // 如何形成闭包 var ali = document.getElementsByTagName("li"); for(var i=0;i<10;i++){ (function(a){ ali[a].onclick = function(){ console.log(a); } })(i) } // 一旦内部函数调用外部函数的局部变量,那么这个时候,这个局部变量就会变成内部函数的私有变量 // 2、当需要给setTimeout的回调函数传参时: setTimeout(function (a){ console.log(a); //两秒后,undefined }, 2000); // 或者 setTimeout(fn(10), 2000); function fn(a){ console.log(a); //没有延迟,直接打印10 } // 怎么使用闭包: function fn(a){ return function(){ console.log(a); //两秒后,10 } } var f = fn(10); setTimeout(f, 2000);
三、闭包的特点:
(1)闭包是将函数内部和函数外部连接起来的桥梁.
(2)可以读取函数内部的变量。
(3)让这些变量的值,始终保存在内存中,不会在调用结束后被系统回收。
(4)避免全局变量命名空间的污染。
(5)内存消耗很大,不能滥用。
(6)闭包会在父函数外部,改变父函数内部变量的值。
四、构造函数继承:
// 在构造函数中,同样属于两个新创建的函数,也是不相等的 function Fn(name){ this.name = name; this.show = function(){ alert(this.name); } } var obj1 = new Fn("AAA"); var obj2 = new Fn("BBB"); obj1.show() obj2.show() // 此时,任何一个new出来的实例上都有了show方法,可以视为最基础的继承。
五、js中的call和apply继承:
function Father(skill){ this.skill = skill; this.show = function(){ alert("我会"+this.skill); } } function Son(abc){ //这里的this指向函数Son的实例化对象 //将Father里面的this改变成指向Son的实例化对象,当相遇将father里面所有的属性和方法都复制到了son身上 //Father.call(this,abc);//继承结束,call适合固定参数的继承 //Father.apply(this,arguments);//继承结束,apply适合不定参数的继承 } var f = new Father("绝世木匠”); var s = new Son("一般木匠"); f.show() s.show(); // 优点: // 创建子类实例时,可以向父类的构造器传参; // 缺点: // 只能继承构造器中定义的属性和方法,不能继承原型上定义的属性和方法
六、js中prototype的概念:
(1)prototype是原型对象
(2)指针 constructor 表示当前函数属于谁,用来指向当前原型所属的函数
(3)原型指针 [[prototype]] __proto__ 相当于一根原型指针,指向当前对象的“父级”
(4)可以在当前函数的原型身上添加各种属性和方法,以供使用
(5)JS中万物皆对象,所有内容的指针终点都指向Object
七、原型链继承:
// 方法一: Son.prototype = new Father(); //创建一个函数Father,用来做原始对象 function Father(){ this.skill = "铁匠" }; Father.prototype.show = function(){ console.log(this.skill) } //创建一个函数Son,准备继承Father的原型 function Son(){}; //将Son点原型,赋值为一个指针,指向Father的原型 Son.prototype = new Father(); //此时就可以通过执行new Son()获取到从构造函数Father上继承过来属性和方法 var s = new Son() s.show(); //铁匠 // 优点: // 1.可以访问父类原型上的方法和属性 // 2.简单方便,易于实现 // 缺点: // 1.创建子类实例时,无法向父类的构造器传参 // 方法二: //创建一个函数Father,用来做原始对象 function Father(){ this.skill = "铁匠"; }; Father.prototype.show = function(){ console.log("hello world") } //创建一个函数Son,准备继承Father的原型 function Son(){}; //将Son的原型,设置为Father的原型 //Son.prototype = Father.prototype; //但是这种拷贝方式为对象的浅拷贝,一旦后期修改Son原型上的方法,会影响到Father的原型 //需采用对象的深拷贝方法 for(var i in Father.prototype){ Son.prototype[i] = Father.prototype[i]; } Son.prototype.show = function(){ console.log("这是子类修改之后的show方法") } var f = new Father() f.show() var s = new Son() s.show(); // 优点: // 完全将父类原型上的方法和属性拷贝到子类原型 // 缺点: // 只能继承原型上的方法和属性,构造函数无法传参和继承
八、混合继承:
// 特点: // 使用call或apply继承父类的构造器中的内容,使用原型继承,继承父类的原型 function Father(skill,id){ this.skill = skill; this.id = id; } Father.prototype.show = function(){ alert("我是father,这是我的技能"+this.skill); } function Son(){ Father.apply(this,arguments); } //如果不做Son的原型继承Father的原型,此时会报错:son.show is not a function for(var i in Father.prototype){ Son.prototype[i] = Father.prototype[i]; } //因为,如果不让Son的原型等于Father的原型,Son使用apply是继承不到原型上的方法 Son.prototype.show = function(){ alert("我是son,这是我的技能"+this.skill); } var f = new Father("专家级铁匠","father"); var s = new Son("熟练级铁匠","son"); f.show(); s.show();
九、ES6中class的继承:
class Father{ constructor(){ } show(){ } } class Son extends Father{ constructor(){ super() } show(){ } }
十、原型对象相关概念解析:
原文:https://www.cnblogs.com/xushipobk/p/11668198.html