整理

104 阅读8分钟
  1. 闭包:创建一个独立环境,每个闭包的环境都是相互独立的,互不干扰。
  • 每次外部函数执行的时候都会开辟一块独立的内存空间,外部函数地址不同,会重新创建一个新的地址。

  • 内部函数总是可以访问其所在的外部函数所声明的参数和变量,即使在其外部函数被返回(终结)之后

  • 特点

  • 外部函数访问内部变量成为可能

  • 局部变量常驻内存之中——长时间占据内存,容易发生内存泄漏

  • 避免使用全局变量,使全局变量污染

  • 优点

  • 保护函数内部的变量,实现封装;防止与其他变量的命名冲突

  • 长时间存在于内存之中,可做缓存使用

  • 匿名自执行函数可以减少内存消耗

  • 缺点

  • 私有变量不能销毁,长时间存在于内存之中,内存消耗;

  • 闭包会涉及到跨域,导致性能损失;可通过将跨作用域变量存储在局部变量中,直接访问局部变量,来减少对性能的损耗

  • 一般情况下外部调用函数执行完毕后,会连同里面的变量一起被销毁;但在此匿名函数作为返回值复制给了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);
      }
    
  1. 作用域/作用域链
  • 作用域:即在运行代码中变量、函数、对象的可访问范围。

  • 全局作用域:没用通过var声明的变量;浏览器中指window;最外层函数和在最外层函数外面定义的变量

  • 局部作用域:又称为函数作用域,即只能在函数内部访问的变量、函数

  • 作用域链:在JS代码运行中,所用到的变量都需要在AO/VO中去查找;当找不到的时候就需要向上层的Execution Context的AO/VO中去查找。这样层层向上查找的过程就是Execution Context AO/VO组成的作用域链

  • 参考文档:www.cnblogs.com/wilber2013/…

  1. this
  • 全局环境下指 window

  • 严格模式下是undefined

  • DOM事件中指触发事件的DOM本身

  • 对象函数里面指代调用方法的对象

  • 构造函数里面指代创建的实例

  • 箭头函数this是词法作用域,由上下文确定

  • 不可用用作构造函数,不能new

  • 不可用使用arguments 可以使用rest代替

  • 不能修改this指向,不能使用call、apply、bind

  1. 修改this指向的方法
  • call 、apply

  • bind 

  • new

  • 创建一个空对象

  • 将空对象的原型指向要new的对象

  • 将空对象绑定到构造函数上

  • 返回空对象

  1. 封装(设计模式)
  • 单例模式:全局唯一的实例。弹窗

  •   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...');
      });
    
  1. 继承:原型链作为实现继承的主要方法
  • www.jianshu.com/p/50469a246…

  • 什么是原型链

  • 所有的引用类型(数组、对象、函数)都存在对象的特点,都具有自由扩展的属性。

  • 所以的引用类型都有 _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是子类。