2017.12.29会议记录

闭包

一、基本概念
闭包:闭包是指有权访问另一个函数作用域中的变量的函数。
创建方式:创建闭包的常见方式,就是在一个函数内部创建另一个函数。

二、相关概念
执行环境:执行环境(也称为环境)是JS中一个最为重要的一个概念。执行环境定义了变量和函数有权访问的其他数据,决定了他们各自的行为。全局执行环境是最外围的一个执行环境。
作用域链:作用域链的用途是保证对执行环境有权访问的所有变量和函数的有序访问,作用域链的前端始终都是当前执行的代码所在环境的变量对象。
标识符解析:标识符解析是沿着作用域链一级一级搜索标识符的过程,搜索过程始终从作用域链前端开始,逐级向后搜索,直到找到标识符为止,如果没找到那就是undefined了。

三、闭包的使用

有编号为1-100的100个按钮,实现点击每一个按钮弹出其对应的序号的交互效果


var buttons = document.querySelectorAll('button')
for(var i = 0; i < buttons.length; i++){
    buttons[i].onclick = function(){
        alert(i+1)
    }
}

代码分析:变量i是var命令声明的,在全局范围内都有效,所以全局只有一个变量i。每一次循环,变量i的值都会发生改变,所以所有的按钮点击事件引用的都是同一个变量 i 。当整个for循环执行完毕后,变量i的值变为100,所以弹出的都是101这个数。但是,我们可以通过创建另一个匿名函数强制让闭包的行为符合预期。


var buttons = document.querySelectorAll('button')
for(var i = 0; i function(num){
      return (function(){
          alert(num+1)
      })
   })(i)
}

代码分析:定义了一个匿名函数,并将立即执行这个函数之后的结果赋给绑定事件。这里的匿名函数有一个参数 num,在调用每个匿名函数时,我们传入了变量 i。由于函数参数是按值传递的,所以就会将变量 i 的当前值复制给参数 num。而在这个匿名函数内部,又创建并返回了一个访问 num 的闭包。这样一来,每个点击事件的回调函数都有自己num 变量的一个副本,因此就可以返回各自不同的数值了。


var buttons = document.querySelectorAll('button')
for(let i = 0; i < buttons.length; i++){
    buttons[i].onclick = function(){ 
        alert(i+1)
    }
}

代码分析:上面代码中,变量i是let声明的,当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新的变量,所以最后能实现我们想要的效果。你可能会问,如果每一轮循环的变量i都是重新声明的,那它怎么知道上一轮循环的值,从而计算出本轮循环的值?这是因为 JavaScript 引擎内部会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的基础上进行计算。块级作用域的出现,实际上使得获得广泛应用的立即执行函数表达式不再必要了。

JavaScript的设计模式

一、构造函数模式
构造函数用于创建特定类型的对象——不仅声明了使用的对象,构造函数还可以接受参数以便第一次创建对象的时候设置对象的成员值。
所谓”构造函数”,其实就是一个普通函数,但是内部使用了this变量。对构造函数使用new运算符,就能生成实例,并且this变量会绑定在实例对象上。

unction Cat(name,color){
  this.name=name;
  this.color=color;
 // this.eat = function(){alert("吃老鼠");};
}
var cat1 = new Cat("大毛","黄色");
var cat2 = new Cat("二毛","黑色");

alert(cat1.name); // 大毛
alert(cat1.color); // 黄色

cat1.eat(); // 吃老鼠
cat2.eat(); // 吃老鼠

这时cat1和cat2会自动含有一个constructor属性,指向它们的构造函数。

     alert(cat1.constructor == Cat); //true

instanceof运算符,验证原型对象与实例对象之间的关系

    alert(cat1 instanceof Cat); //true

对于每一个实例对象,eat()方法都是一模一样的内容,每一次生成一个实例,都必须为重复的内容,多占用一些内存。这样既不环保,也缺乏效率

优点:每个实例的公共对象都是不同的,不会相互影响。

二、原型模式
原型模式(prototype)是指用原型实例指向创建对象的种类,并且通过拷贝这些原型创建新的对象。
Javascript中没有类的概念,就算ES6中引入的class也不过是一种语法糖,本质上还是利用原型实现。在原型编程语言中,类并不是必需的,对象不一定需要由类实例化而来,而是通过克隆另外一个对象来得到。

原型模式是用来创建对象的一种模式。在以类为中心的语言中,要创建一个对象首先要指定这个对象的类型,然后实例化一个对象。使用原型模式创建对象时不必关心对象的具体类型,而是找到一个对象,然后通过克隆来创建一个一模一样的对象。

function Master(){
    this.blood = 100;
    this.level = 6;
}
var noumenon = new Master();
noumenon.level = 9;
var ektype = Object.create(noumenon);
console.log(ektype);

通过以上代码,我们看到了如何通过原型模式来克隆出一个一模一样的的对象。原型模式的真正意义并非创建一个一模一样的对象,而是提供一种创建对象的方式,Javascript的面向对象机制是基于原型模式的,他的对象系统就是使用原型模式,通过克隆来创建的,克隆是创建一个对象的过程和手段。以继承为例:

function Person(name){
    this.name = name;
}
function Developer(lang){
    this.language = lang;
}
var p = new Person('coder');
Developer.prototype = p;
var dev = new Developer('Javascript');

基于原型的继承体系,子类的每次实例化都是对其构造函数的prototype属性的克隆。所以每次创建Developer对象,其实都是在对p对象的克隆。

在Java等以类为中心的面向对象语言中,经常使用new实例化一个对象。但是Javascript是基于原型的面向对象语言,在这里new运算符创建对象的方式与Java中的new运算符并不相同,Javascript中的new运算符也是通过克隆来实例化对象的,克隆的是构造器函数的原型对象,new运算符的作用等同于如下代码:

function Person(name){
    this.name = name;
}
function Developer(lang){
    this.language = lang;
}
var p = new Person('coder');
Developer.prototype = p;
function _new(_Constructor) {
    var that = Object.create(_Constructor.prototype);
    var args = Array.prototype.slice.call(arguments, 1);
    var other = _Constructor.apply(that, args);
    return (typeof other === 'object' && other) ? other : that;
}
_new(Developer, 'JavaScript')

从这我们也可以看出,Javascript的原型实际上存在着诸多矛盾,它的某些复杂语法看起来就像那些基于类的语言,这掩盖了它的原型机制。所以jQuery中尽量避免使用new运算符来创建对象。

Prototype模式的验证方法:

function Cat(name,color){
  this.name = name;
  this.color = color;
}
Cat.prototype.type = "猫科动物";
Cat.prototype.eat = function(){alert("吃老鼠")};
var cat1 = new Cat("大毛","黄色");
var cat2 = new Cat("二毛","黑色");
alert(cat1.type); // 猫科动物
cat1.eat(); // 吃老鼠
  • isPrototypeOf()

    判断某个proptotype对象和某个实例之间的关系

 alert(Cat.prototype.isPrototypeOf(cat1)); //true
 alert(Cat.prototype.isPrototypeOf(cat2)); //true
  • hasOwnProperty()

    用来判断某一个属性到底是本地属性,还是继承自prototype对象的属性

alert(cat1.hasOwnProperty("name")); // true
alert(cat1.hasOwnProperty("type")); // false
  • in运算符
    用来遍历某个对象的所有属性
for(var prop in cat1) { alert("cat1["+prop+"]="+cat1[prop]); }

原型模式优点
1. 减少内存消耗,系统资源占用少,所有实例共享同一方法,不会创建多个
2. 原型对象继承时,子类在重写父类原型方法时很方便 ,可以很方便 调父类房法,再扩展。
原型模式缺点
1. 优点1既是最大的优点,也同样带来一个严重问题,如果共享的对象是引用 对象(如array)则也会造成多个实例共享同一个array,很可能会相互影响

你可能感兴趣的:(会议内容)