一、闭包
- 通过作用域的嵌套,触发计算机的垃圾回收机制(硬盘),将原来的局部变量进化成私有变量的环境叫闭包
私有变量:
- JavaScript没用私有属性的概念;所有的属性都是公用的;
- 私有变量的概念:在任何函数中定义的变量,都是私有变量,因为不能在函数外部访问这些变量;
- 私有变量:包括函数的参数/局部变量和在函数内部定义的其他函数;
- 特权方法:内部创建一个闭包,闭包可以访问私有变量;因此创建用于访问私有变量的公用方法,称作特权方法
- 可以通过构造方法传参来访问私有变量
这种方法的缺点是会为每一个实例创建一组新的方法,不能实现共享。
计算机的垃圾回收机制
硬盘
:需要删除的数据,不直接删除,而是保存在一个临时区域,如果需要继续使用,可以从临时区域取出
内存
:需要删除的数据,直接删除,不可恢复
程序执行默认使用内存的垃圾回收
函数的定义作用域和执行作用域
- 定义函数时的作用域,叫定义作用域
- 执行函数时的作用域,叫做执行作用域
-无论函数在哪里执行,都可以在自身内部使用自身定义作用域中的数据
闭包的特点
- 可以在作用域外部修改作用域内部的数据
- 连接了作用域内部的桥梁
- 消耗内存
- 内存泄漏(低版本浏览器)
闭包的应用场景
- 事件委托的封装
// 1. 事件委托的函数因为要传参,导致会被立即执行
function eveEnt(child, cb){
// 2. 所以给原函数添加返回值新函数,作为真正的事件处理函数
return function(eve){
// 3. 处理事件对象
var e = eve || window.event
// 4. 处理事件目标
var tar = e.target || e.srcElement;
// 5. 为了更方便的自定义要委托事件的元素,将整个元素对象都作为参数传进来
// 遍历,和事件目标匹配
for(var i=0;i<child.length;i++){
// 6. 如果触发事件时,在传进来的参数的数组中,找到了与事件目标元素相同的对象,说明,触发到了要委托事件的元素
if(child[i] === tar){
// 7. 就可以执行外部传入的功能
// 8. 为了让外部通过this方便的拿到触发事件的元素,所以借助函数的方法,将外部传入的功能函数的this指向改成事件目标
cb.call(tar);
}
}
}
}
- 循环中的事件内使用循环的变量
// // 循环中的事件内使用循环的变量
var ali = document.querySelectorAll("li")
for(var i=0;i<ali.length;i++){
(function(i){
ali[i].onclick = function(){
console.log(i);
}
})(i);
}
for(var i=0;i<ali.length;i++){
ali[i].onclick = (function(i){
return function(){
console.log(i);
}
})(i);
}
- 可以给内置的方法的回调函数传参
setTimeout(fn("hello"), 1000);
function fn(a){
return function(){
console.log(a);
}
}
二、继承
继承
- 让一个不具有某些功能或属性的类或对象,通过某些方式使用另一个具有这些功能或属性的类或对象的功能或属性
基本继承
var obj1 = {
name:"admin",
show:function(){
console.log(this.name)
}
}
var obj2 = {
name:"root"
}
obj1.show();
obj1.show.call(obj2);
原型继承
- 原型对象继承
只能继承原型身上的属性或方法,无法继承构造函数身上的属性或方法
function Parent(){
this.name = "admin";
}
Parent.prototype.show = function(){
console.log(this.name)
}
function Child(){
this.name = "root"
}
// 注意深浅拷贝
// Child.prototype = Parent.prototype;
for(let i in Parent.prototype){
Child.prototype[i] = Parent.prototype[i]
}
Child.prototype.show = function(){
console.log("这是改写之后的show")
}
var p = new Parent();
p.show();
var c = new Child();
c.show();
原型链继承
function Parent(n){
this.name = n;
}
Parent.prototype.show = function(){
console.log(this.name)
}
function Child(){
}
Child.prototype = new Parent("张四");
var p = new Parent("张三");
p.show();
console.log(p)
var c = new Child();
c.show();
console.log(c)
// 实例->__proto__->构造该实例的函数的prototype
// 实例->__proto__->父级的实例->__proto__->构造父级实例的函数的prototype
构造函数继承
function Parent(n, s){
this.name = n;
this.sex = s;
this.show = function(){
console.log(this.name + "====" + this.sex);
}
}
Parent.prototype.init = function(){
console.log("hello")
}
function Child(n, s){
Parent.call(this, n, s)
// Parent.apply(this, [n, s])
// Parent.bind(this, n, s)();
// A.call(this)
// B.call(this)
}
var p = new Parent("张三", "男")
p.show();
p.init();
var c = new Child("张四", "女");
c.show();
c.init();
混合继承
function Parent(n, s){
this.name = n;
this.sex = s;
}
Parent.prototype.show = function(){
console.log(this.name + "----" + this.sex)
}
function Child(n, s){
Parent.call(this, n, s);
}
for(let i in Parent.prototype){
Child.prototype[i] = Parent.prototype[i];
}
1
var p = new Parent("张三", "男");
p.show();
console.log(p)
var c = new Child("张四", "男");
c.show();
console.log(c)
class继承
class Parent{
constructor(n){
this.name = n;
}
show(){
console.log(this.name);
}
}
class Child extends Parent{
constructor(n){
super(n);
}
}
var p = new Parent("张三");
p.show();
console.log(p);
var c = new Child("张四");
c.show();
console.log(c);
// 父类和子类
// 子类继承了父类
// 父类相对于子类,叫超类
实例和构造函数之间的关系检测
function A(){}
function B(){}
function C(){}
C.prototype = new A();
var a = new A();
var b = new B();
var c = new C();
console.log(a.__proto__ === A.prototype);
console.log(a.__proto__.constructor === A.prototype.constructor);
console.log(a.__proto__.constructor === A);
console.log(a.constructor === A);
console.log(A === A.prototype.constructor);