JS核心技术开发解密读书笔记3

第五章 作用域与作用域链

1. 作用域

常见的作用域有两种,全局作用域和函数作用域。ES6中新增了块级作用域。
全局作用域中声明的变量与函数可以在代码的任何地方被访问。全局作用域有以下三种情形:

1)全局对象下拥有的属性与方法

window.name;
window.location;
window.top;
……

2)在最外层声明的变量与方法

var foo = function () {} ;
var str = 'out variable'; 
var arr = [1, 2, 3]; 
function bar () {}

3)在非严格模式下,函数作用域中未定义但直接复制的变量与方法,即函数fn可以直接使用全局作用域里面的变量

function foo () {
  bar = 20;
}
function fn () {
  foo();
  return bar + 30;
}
fn(); // 50

函数作用域中声明的变量与方法,只能被下层子作用域访问,而不能被其他不相干的作用域访问。

function foo () {
  var a = 20;
  var b = 30;
}
foo();
function bar () {
  return a + b;
}
bar(); // Uncaught ReferenceError

因为作用域的限制,bar中无法访问变量a和b,因此函数执行报Uncaught ReferenceError。

function foo () {
  var a = 20;
  var b = 30;
  function bar () {
    return a + b;
  }
  return bar();
}
foo(); // 50

bar中作用域为foo的自作用域,因此能访问到变量a和b。
块级作用域通过ES6新增的let和const来体现。

function f1 () {
  let n = 5;
  if (true) {
    let n = 10;
  }
  console.log(n); // 5
}

上面的函数有两个代码块,都声明了变量n,运行后输出5,这表示外层代码块不收内层代码块的影响。如果两次都使用var定义变量n,最后输出的值才是10。

2. 立即执行函数(Immediately Invoked Function Expression, IIFE)

块级作用域出现前,常使用IIFE去模拟块级作用域。

var a = 2;
(function foo () {
  var a = 3;
  console.log(a); // 3
})();
console.log(a); // 2

由于函数被包含在一对括号内部,因此成为了一个表达式,通过在末尾加上了另外一个括号可以立即执行这个函数。
IIFE另一个皮鞭的进阶用法是把它们当做函数调用并传递参数进去。

var a = 2;
(function IIFE (global) {
  var a = 3;
  console.log(a); // 3
  console.log(global.a); // 2
})(window);
console.log(a); // 2

我们将window对象的引用传递进去,但将参数命名为global,因此在代码风格上对全局对象的引用变得比引用一个没有“全局”字样的变量更加清晰。了解更多块级作用域的知识,请点击这里

第六章 闭包

1.应用闭包和循环(闭包循环相关问题)

for(va r i = 1;i <= 5;i++){
    setTimeout(function timer(){
       console.log(i);
    },i*1000);
 }

这道题相比很多人都见过,会造成的结果是每一个值都输出6。而我们想隔秒输出1,2,3,4,5,6,则需要产生模拟块级作用域,把每一个的i值保存在一个闭包里面,当setTimeout中定义操作执行时,访问对应的闭包即可。

方法1:

for(var i = 1;i <= 5;i++){
   (function(i){
       setTimeout(function timer(){
          console.log(i);
       },i*1000);
    })(i);
 }

方法2:

for(var i  =1;i <= 5;i++) {
    setTimeout(funcyion(i){
      return function yimer(){
        console.log(i);
      }
    })(i),i*1000);
 }

2.单例模式与闭包(设计模式:单例模式,工厂模式,观察者模式,装饰模式,原型模式,建造者模式,桥接模式,组合模式等)

单例模式,所谓的单例,就是只有一个实例。

实现过程:

1.可以将属性和方法放入到字面量里面,但是他的属性可以被外部修改,这往往不符合我们的需求。

var person = {

    name  = "pengchangxi";
    age = 22;

    getName:function(){
      return this.name;
   },
    getAge:function(){
      return this.age;
   }
 }

2.有自己的私有方法/属性(创建一个小型代码库)

var person = (function() {
     var name = "pengchangxi";
     var age  =22;

     return {
         getName:funcyion(){
            return name;    
        }

        getAge:function(){
            return age;
         }
  }
 })();
//访问私有变量
 person.getAge();

3.调用时初始化

var person =  (function() {

    //定义一个变量,用来保存实例

     var instancce = null;

     var name  = "pengchangxi";

     var age = 22;

     //初始化方法

     fucntion initial(){

         return {

            getName:function(){return name;},

            getAge:function() {return age},

         }

     }

     return {

        getInstance:fucntion(){

            if(!instance) {

                instance  =initial();

            }

         return instance;

        }

    }

 })();

 //只有在使用的时候获得实例对象

 var p1 = person.getInstance();

 var p2 = person.getInstance();

 console.log(p1===p2)

总结一下,关键的地方在于,在匿名函数中定义的instance变量只是用来保存实例,在getInstance方法中判断了是都对它进行重新赋值,当instance被第一次调用的时候他就会被赋initial的值,所以这是一种单例模式。

3. 模块化和闭包

模块化是建立在单例模式上面的,而且模块化与闭包息息相关。

1.每一个单例就是一个模块.

2.每一个模块要与其他模块交互,则必须有获取其他模块的能力,比如requirejs中的require和ES6 modules中的import

require

 var $  =require('jquery');

 //ES6 modules

 import $ from 'jquery';

3.每一个模块都有一个对外接口,以保证与其他模块交互的能力。

var module_test = (function() {


   return {

      testfn1: function(){},

      testfn1: function(){},

   }

 })();

总结一下js模块化开发的流程(实现一个body背景色随着数字的递增在固定的三种颜色之间切换的效果)

(1)首先创建一个管理全局状态的模块,这个模块有自己的方法和属性,并且提供一个对外接口来保证外部访问内部私有属性和方法。

var module_status = (function() {

 var status = {

    number: 0,

    color: null

 }

 var get = function(prop) {

     return status[prop]; 

    }

 var set =function(prop, value){

     status [prop] =value;

 }

 return {

    get: get,

    set: set

   }

 })();

(2)创建一个模块,这个模块专门负责满足需求的实现(负责body背景色的改变)。

var module_color = (function() {

 **//假she用这种方式执行第二步引入模块,

 //类似于import state from 'module-status'**

 var state = module_status;

 var colors = ['orange', '#ccc', 'pink'];

 function render() {

    var color = colors[state.get ('number') % 3];

    document.body.style.backgroundColor = color;

 }

 return {

    render: render

   }

 })();

(3)创建另外一个模块,负责显示当前的number值,用于参考和对比

var module_context = (function() {

     var state = module_status;

     function render() {

        document.body.innerHTML ='this Number is '+ state.get ('number ');

     }

     return {

       render: render

     }

 })();

(4)这些功能模块都创建完毕后,最后我们只需要穿创建一个主模块,这个主模块的目的是借助这些功能莫魁岸,来实现我想要实现的效果

var module_main = (function() {

     var state = module_status;

     var color = module_color;

     var context = module_context;

     setInterval(function() {

        var newNumber = state.get ('number') + 1;

        state.set('number', newNumber);

        color.render();

        context.render();

    }, 1000);

 })();

完整代码(初步学习和实现js模块化):

<script>
 var module_status = (function() {

 var status = {

    number: 0,

    color: null

 }

 var get = function(prop) {

     return status[prop]; 

    }

 var set =function(prop, value){

     status [prop] =value;

 }

 return {

    get: get,

    set: set

   }

 })();

 var module_color = (function() {

 **//假she用这种方式执行第二步引入模块,

 //类似于import state from 'module-status'**

 var state = module_status;

 var colors = ['orange', '#ccc', 'pink'];

 function render() {

    var color = colors[state.get ('number') % 3];

    document.body.style.backgroundColor = color;

 }

 return {

    render: render

   }

 })();

 var module_context = (function() {

     var state = module_status;

     function render() {

        document.body.innerHTML ='this Number is '+ state.get ('number ');

     }

     return {

       render: render

     }

 })();

 var module_main = (function() {

     var state = module_status;

     var color = module_color;

     var context = module_context;

     setInterval(function() {

        var newNumber = state.get ('number') + 1;

        state.set('number', newNumber);

        color.render();

        context.render();

    }, 1000);

 })();
</script>

以上乃模块化开发学习的初步过程,后续有待完善,如有错误,敬请指出!

全部评论

相关推荐

点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务