- 闭包:创建一个独立环境,每个闭包的环境都是相互独立的,互不干扰。
-
每次外部函数执行的时候都会开辟一块独立的内存空间,外部函数地址不同,会重新创建一个新的地址。
-
内部函数总是可以访问其所在的外部函数所声明的参数和变量,即使在其外部函数被返回(终结)之后
-
特点
-
外部函数访问内部变量成为可能
-
局部变量常驻内存之中——长时间占据内存,容易发生内存泄漏
-
避免使用全局变量,使全局变量污染
-
优点
-
保护函数内部的变量,实现封装;防止与其他变量的命名冲突
-
长时间存在于内存之中,可做缓存使用
-
匿名自执行函数可以减少内存消耗
-
缺点
-
私有变量不能销毁,长时间存在于内存之中,内存消耗;
-
闭包会涉及到跨域,导致性能损失;可通过将跨作用域变量存储在局部变量中,直接访问局部变量,来减少对性能的损耗
-
一般情况下外部调用函数执行完毕后,会连同里面的变量一起被销毁;但在此匿名函数作为返回值复制给了f1,此时相当于f1 = function(){var num2 = 0; ...}且里面引用了外部函数的变量num,num无法销毁,而num2每次都是新建的;f1执行之后回销毁相关的函数,而num一直无法销毁,导致内存长期被占...
-
function fn(){ var num = 3; return function(){ var num2 = 0; console.log(++num2); console.log(++num); } } var f1=fn() f1(); 1 4 f1(); 1 5 经典循环案例 for(var i = 1; i<5;i++){ (function(i){ setTimeOut(function(){console.log(i)},2000); })(i); }
- 作用域/作用域链
-
作用域:即在运行代码中变量、函数、对象的可访问范围。
-
全局作用域:没用通过var声明的变量;浏览器中指window;最外层函数和在最外层函数外面定义的变量
-
局部作用域:又称为函数作用域,即只能在函数内部访问的变量、函数
-
作用域链:在JS代码运行中,所用到的变量都需要在AO/VO中去查找;当找不到的时候就需要向上层的Execution Context的AO/VO中去查找。这样层层向上查找的过程就是Execution Context AO/VO组成的作用域链
- this
-
全局环境下指 window
-
严格模式下是undefined
-
DOM事件中指触发事件的DOM本身
-
对象函数里面指代调用方法的对象
-
构造函数里面指代创建的实例
-
箭头函数this是词法作用域,由上下文确定
-
不可用用作构造函数,不能new
-
不可用使用arguments 可以使用rest代替
-
不能修改this指向,不能使用call、apply、bind
- 修改this指向的方法
-
call 、apply
-
bind
-
new
-
创建一个空对象
-
将空对象的原型指向要new的对象
-
将空对象绑定到构造函数上
-
返回空对象
- 封装(设计模式)
-
单例模式:全局唯一的实例。弹窗
-
var single = (function(){ let instance; function getInstance(){ // 如果该实例存在,则直接返回,否则就对其实例化 if( instance=== undefined ){ instance= new Construct(); } return instance; } function Construct(){ // ... 生成单例的构造函数的代码 } return { getInstance : getInstance } })(); -
工厂模式
-
//安全模式创建的工厂方法函数 let UserFactory = function(role) { if(this instanceof UserFactory) { var s = new this[role](); return s; } else { return new UserFactory(role); } } //工厂方法函数的原型中设置所有对象的构造函数 UserFactory.prototype = { SuperAdmin: function() { this.name = "超级管理员", this.viewPage = ['首页', '通讯录', '发现页', '应用数据', '权限管理'] }, Admin: function() { this.name = "管理员", this.viewPage = ['首页', '通讯录', '发现页', '应用数据'] }, NormalUser: function() { this.name = '普通用户', this.viewPage = ['首页', '通讯录', '发现页'] } } //调用 let superAdmin = UserFactory('SuperAdmin'); let admin = UserFactory('Admin') let normalUser = UserFactory('NormalUser') -
代理模式
-
var myImage = (function(){ var imgNode = document.createElement( 'img' ); document.body.appendChild( imgNode ); return { setSrc: function( src ){ imgNode.src = src; } } })(); var proxyImage = (function(){ var img = new Image; img.onload = function(){ myImage.setSrc( this.src ); } return { setSrc: function( src ){ myImage.setSrc( 'file:// /C:/Users/svenzeng/Desktop/loading.gif' ); img.src = src; } } })(); proxyImage.setSrc('图片地址'); -
发布订阅模式
-
var EventCenter = (function(){ var events = {}; function on(event, handler){ events[event] = events[event] || []; events[event].push({ handler: handler }); } function fire(event, args){ if (!events[event]) {return} for (var i = 0; i < events[event].length; i++) { events[event][i].handler(args); } } function off(event){ delete events[event]; } return { on: on, fire: fire, off: off } })(); EventCenter.on('event', function(data){ console.log('event received...'); });
- 继承:原型链作为实现继承的主要方法
-
-
什么是原型链
-
所有的引用类型(数组、对象、函数)都存在对象的特点,都具有自由扩展的属性。
-
所以的引用类型都有 _proto_这个隐藏属性。是一个普通的对象
-
所以的函数都有prototype的属性。是一个普通的函数
-
所以的引用类型的_proto_属性指它的构造函数的prototype。
-
当查找某一个属性的时候,首先会在它本身查找。没有的话回去它的_proto_(即构造函数的prototype)上查找,没有找到的时候会层级向上查找,直到追溯到Object.prototype。其实也就是说每个对象都是继承自Object.prototype,而Object.prototype的原型是null,null没有任何的属性和方法
-
原型链
-
对象之间的继承的关系在js中是通过prototype对象指向父类对象,直到指向Object对象为止,这样形成的原型的指向链条成为原型链
-
function Animal(name){ this.name = name;//属性 this.eat = function(){//实例方法 console.log(this.name+'正在吃东西'); } } Animal.prototype.sleep = function(){} -
原型链继承:父类的实例作为子类的原型;在子类的原型上实例化父类,在子类构造函数中执行一遍父类的构造函数;
-
特点
-
实例既是子类的实例也是父类的实例
-
父类的原型上的方法属性子类都可以访问的到
-
缺点
-
子类添加自己的属性不能放在构造方法中,要在原型指向之后添加;
-
无法实现多继承
-
原型上的所有属性和方法在所以的实例里面共享
-
创建子类实例的时候无法向父类传参数
-
function Cat(){} Cat.prototype = new Animal(); Cat.prototype.name = 'cat'; var cat = new Cat(); console.log(cat.name); console.log(cat.eat('fish')); console.log(cat.sleep()); console.log(cat instanceof Animal); //true console.log(cat instanceof Cat); -
构造函数继承:call改变函数的作用环境;在子类中调用就是将子类的属性方法在父类中执行,父类给this绑定属性,因而继承了父类的共有属性
-
特点
-
子类实例可以向父类传参数
-
可以实现多继承(call多个父类就行了)
-
缺点
-
子类实例不是父类的实例
-
子类只能继承父类实例的属性和方法,不能继承原型上的属性和方法
-
无法实现函数的复用;每个子类都有父类实例函数副本,影响性能
-
function Cat(name){ Animal.call(this); this.name = name; } // Test Code var cat = new Cat(); console.log(cat.name); console.log(cat.sleep()); console.log(cat instanceof Animal); // false console.log(cat instanceof Cat); // true -
组合继承
-
特点
-
通过调用父类的构造函数,继承父类的属性和方法;通过将原型指向父类的实例,实现函数的复用
-
优点
-
可以继承实例的属性和方法;也可以继承原型的属性和方法
-
既是子类实例也是父类实例
-
可以传参数
-
函数可以复用
-
缺点:调用了两次父类的构造函数,生成两份实例
-
function Cat(name){ Animal.call(this); this.name = name || 'tom'; } Cat.prototype = new Animal(); -
原型继承:由于是浅克隆,引用类型的数据共享在不同的实例之间。
-
function object(o){ function F () {}; F.prototypt = new o; return new F(); } var obj = { name:'tom' } var obj2 = object(obj); -
es5中新增了一个函数Object.create()来实现原型继承
-
var obj = { name:'tom' } var obj2 = Object.create(obj); -
寄生继承
-
function Object(o){ var obj = Object.create(o); obj.sayHi = function(){ console.log('Hi') } return obj; } var person = { name:'tom' } var p2 = Object(person); p2.sayHi() //Hi -
寄生组合继承:它只调用了一次超类(父类)的构造,并且避免了在子类prototype上面创建不必要,多余的属性。
-
function inheritPrototype(child,parent){ var o = Object.create(parent.prototype);//父类原型的浅复制 o.constructor = child; // 修正子类原型的构造函数属性;constructor是对象才拥有的属性,它从一个对象指向一个函数,含义为指向该对象的构造函数;未修改前指向的 parent,为了弥补因重写原型而失去的默认constructor属性。 child.prototype = o;//子类的原型指向父类原型的浅复制 } //--------------------- function Cat(name, age){ Animal.call(this,name); this.age = age; } inheritPrototype(Cat,Animal); Cat.prototype.sayAge = function () { alert(this.age); } var instance = new Cat("jindou"); console.log(instance.name) console.log(instance.constructor) 指向cat的构造函数,如果没有修正 则指向父类的构造函数 -
extends:父类的属性和方法默认添加到子类原型上;本质还是原型链继承
-
super等价于parent.prototype.constructor.call(sub)
-
super:子类必须在constructor方法中调用super方法,如果子类没有定义constructor方法,constructor方法以及其内部的super方法会被默认添加。
-
子类构造方法只要调用super之后才可以使用this,否则报错;super只能在构造函数内使用
-
给super添加属性或者修改,修改的是子类的属性,因为this是子类。