高级JS内容——JavaScript高级程序设计笔记

  1. 安全的类型检测
    由于原生数组的构造函数名与全局作用域无关,因此使用toString()就能保证返回一致的值。利
    用这一点,可以创建如下函数:
    function isArray(value){
    return Object.prototype.toString.call(value) == “[object Array]”;
    }
    同样,也可以基于这一思路来测试某个值是不是原生函数或正则表达式:
    function isFunction(value){
    return Object.prototype.toString.call(value) == “[object Function]”;
    }
    function isRegExp(value){
    return Object.prototype.toString.call(value) == “[object RegExp]”;
    }

  2. 作用域安全的构造函数
    构造函数其实就是一个使
    用new 操作符调用的函数。当使用new 调用时,构造函数内用到的this 对象会指向新创建的对象实
    例,如下面的例子所示:
    function Person(name, age, job){
    this.name = name;
    this.age = age;
    this.job = job;
    }
    var person = new Person("Nicholas", 29, "Software Engineer");

    上面这个例子中,Person 构造函数使用this 对象给三个属性赋值:name、age 和job。当和new
    操作符连用时,则会创建一个新的Person 对象,同时会给它分配这些属性。问题出在当没有使用new
    操作符来调用该构造函数的情况上。由于该this 对象是在运行时绑定的,所以直接调用Person(),
    this 会映射到全局对象window 上,导致错误对象属性的意外增加。例如:
    var person = Person("Nicholas", 29, "Software Engineer");
    alert(window.name); //"Nicholas"
    alert(window.age); //29
    alert(window.job); //"Software Engineer"

    这里,原本针对Person 实例的三个属性被加到window 对象上,因为构造函数是作为普通函数调
    用的,忽略了new 操作符。这个问题是由this 对象的晚绑定造成的,在这里this 被解析成了window
    对象。由于window 的name 属性是用于识别链接目标和frame 的,所以这里对该属性的偶然覆盖可能
    会导致该页面上出现其他错误。这个问题的解决方法就是创建一个作用域安全的构造函数。
    作用域安全的构造函数在进行任何更改前,首先确认this 对象是正确类型的实例。如果不是,那
    么会创建新的实例并返回。请看以下例子:
    function Person(name, age, job){
    if (this instanceof Person){
    this.name = name;
    this.age = age;
    this.job = job;
    } else {
    return new Person(name, age, job);
    }
    }
    var person1 = Person("Nicholas", 29, "Software Engineer");
    alert(window.name); //""
    alert(person1.name); //"Nicholas"
    var person2 = new Person("Shelby", 34, "Ergonomist");
    alert(person2.name); //"Shelby"

  3. 函数绑定
    函数绑定要创建一个函数,可以在特定的this 环境中以指定参数调用另一个函数。该技巧常常和回调函数与事件处理程序一起使用,以便在将函数作为变量传递的同时保留代码执行环境。请看以下例子:
    var handler = {
    message: “Event handled”,
    handleClick: function(event){
    alert(this.message);
    }
    };
    var btn = document.getElementById(“my-btn”);
    EventUtil.addHandler(btn, “click”, handler.handleClick);
    在上面这个例子中,创建了一个叫做handler 的对象。handler.handleClick()方法被分配为
    一个DOM 按钮的事件处理程序。当按下该按钮时,就调用该函数,显示一个警告框。虽然貌似警告框
    应该显示Event handled , 然而实际上显示的是undefiend 。这个问题在于没有保存
    handler.handleClick()的环境,所以this 对象最后是指向了DOM按钮而非handler(在IE8 中,
    this 指向window。)可以如下面例子所示,使用一个闭包来修正这个问题。
    var handler = {
    message: “Event handled”,
    handleClick: function(event){
    alert(this.message);
    }
    };
    var btn = document.getElementById(“my-btn”);
    EventUtil.addHandler(btn, “click”, function(event){
    handler.handleClick(event);
    });
    一个简单的bind()函数接受一个函数和一个环境,并返回一个在给定环境中调用给定函数的函数,
    并且将所有参数原封不动传递过去。语法如下:
    function bind(fn, context){
    return function(){
    return fn.apply(context, arguments);
    };
    }
    var handler = {
    message: “Event handled”,
    handleClick: function(event){
    alert(this.message);
    }
    };
    var btn = document.getElementById(“my-btn”);
    EventUtil.addHandler(btn, “click”, bind(handler.handleClick, handler));

  4. 函数柯里化

    • 用于创建已经设置好了一个或多个参数的函数。函数柯里化的基本方法和函数绑定是一样的:使用一个闭包返回一个函数
    • 柯里化函数通常由以下步骤动态创建:调用另一个函数并为它传入要柯里化的函数和必要参数。下
      面是创建柯里化函数的通用方式。
      function curry(fn){
      var args = Array.prototype.slice.call(arguments, 1);
      return function(){
      var innerArgs = Array.prototype.slice.call(arguments);
      var finalArgs = args.concat(innerArgs);
      return fn.apply(null, finalArgs);
      };
      }

      curry()函数的主要工作就是将被返回函数的参数进行排序。curry()的第一个参数是要进行柯里化的函数,其他参数是要传入的值。为了获取第一个参数之后的所有参数,在arguments 对象上调用了slice()方法,并传入参数1 表示被返回的数组包含从第二个参数开始的所有参数。然后args 数组包含了来自外部函数的参数。在内部函数中,创建了innerArgs 数组用来存放所有传入的参数(又一次用到了slice())。有了存放来自外部函数和内部函数的参数数组后,就可以使用concat()方法将它们组合为finalArgs,然后使用apply()将结果传递给该函数。注意这个函数并没有考虑到执行环境,所以调用apply()时第一个参数是null。curry()函数可以按以下方式应用。
      function add(num1, num2){
      return num1 + num2;
      }
      var curriedAdd = curry(add, 5);
      alert(curriedAdd(3)); //8
  5. 级别1:不可扩展对象
    Object.preventExtensions()方法让你不能再给对象添加属性和方法。
    例如:
    var person = { name: "Nicholas" };
    Object.preventExtensions(person);
    person.age = 29;
    alert(person.age); //undefined

  6. 级别2: 密封的对象
    var person = { name: "Nicholas" };
    Object.seal(person);
    person.age = 29;
    alert(person.age); //undefined
    delete person.name;
    alert(person.name); //"Nicholas"

  7. 最严格的防篡改级别是冻结对象
    冻结的对象既不可扩展,又是密封的,而且对象数据属性的[[Writable]]特性会被设置为false。如果定义[[Set]]函数,访问器属性仍然是可写的。
    var person = { name: "Nicholas" };
    Object.freeze(person);
    person.age = 29;
    alert(person.age); //undefined
    delete person.name;
    alert(person.name); //"Nicholas"
    person.name = "Greg";
    alert(person.name); //"Nicholas"

你可能感兴趣的:(javascript)