JavaScript高等程序设计-择要笔记-7

高等用法

1. 平安的范例检测

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]'
}

这类要领可以检测一个对象是不是是某种范例的原生对象。条件是 Object.prototype.toString() 要领未被修正
在 Web 中可以辨别原生对象和非原生对象很主要。如许才确实晓得某个对象到底有哪些功用。

2. 作用域平安的组织函数

缘由以下:

function Person (name, age, job) {
  this.name = name
  this.age = age
  this.job = job
}
var Person = new Person('wfc', 25, 'frontend')

假如遗忘运用 new 操纵符,那末,组织函数里的this将指向全局window,如许会在window对象上增加分外的属性,能够掩盖其他有效的属性致使毛病。
平安的做法:

function Person (name, age, job) {
  if (this instanceof Person) {
    this.name = name
    this.age = age
    this.job = job
  } else {
    return new arguments.callee(name, age, job)
  }
}

如许处置惩罚后,假如仅采纳组织函数形式来继承,能够会出问题
如:

function Person (name, age, job) {
  this.name = name
  this.age = age
  this.job = job
}
function Student (name, age, job, sex) {
  Person.call(this, name, age, job)
  this.sex = sex
}
var ming = new Student('ming', 12, 'student', 'male')
console.log(ming.name) // undefined

所以采纳作用域平安的组织函数,要求采纳以下步伐

function Person (name, age, job) {
  this.name = name
  this.age = age
  this.job = job
}
function Student (name, age, job, sex) {
  Person.call(this, name, age, job)
  this.sex = sex
}
Student.prototype = new Person()
var gang = new Student('gang', 13, 'student', 'male')
console.log(ming.name) // 'gang'

3. 惰性载入函数

如:

function createXHR () {
  if (typeof XMLHttpRequest != undefined) { // 这里不必 == 来推断,由于差别浏览器下效果不一样,safari 获得的是 'object',其他浏览器是'function'
    return new XMLHttpRequest()
  } else if (typeof ActiveXObject != undefined) {
    if (typeof arguments.callee.activeXString != 'string') {
      var versions = [
        'MSXML2.XMLHttp.6.0', 'MSXML2.XMLHttp.3.0', 'MSXML2.XMLHttp'
      ]
      for (var i = 0, len = versions.length; i < len; i++) {
        try {
          new ActiveXObject(versions[i]);
          arguments.callee.activeXString = versions[i];
          break;
        } catch (ex) {}
      }
    } else {
      return new ActiveXObject(arguments.callee.activeXString)
    }
  } else {
    throw new Error('no XHR object available')
  }
}

注:IE7+ 就原生支撑 XMLHttpRequest 对象。
每次挪用 createXHR() 的时候,它都要对浏览器所支撑的才能进行搜检,但实在只须要初次载入时搜检一次就可以肯定该浏览器支撑哪一个 XHR 对象。
惰性载入示意函数实行的分支仅会发作一次。
要领1:

function createXHR () {
  if (typeof XMLHttpRequest != undefined) {
    createXHR = function () {
      return new XMLHttpRequest()
    }
  } else if (typeof ActiveXObject != undefined) {
    var versions = [
      'MSXML2.XMLHttp.6.0', 'MSXML2.XMLHttp.3.0', 'MSXML2.XMLHttp'
    ]
    for (var i = 0, len = versions.length; i < len; i++) {
      try {
        new ActiveXObject(versions[i]);
        createXHR = function () {
          return new ActiveXObject(versions[i]);
        }
        break;
      } catch (ex) {}
    }
  } else {
    createXHR = function () {
      throw new Error('No XHR object available')
    }
  }
  return createXHR()
}

要领2:

var createXHR = (function () {
  if (typeof XMLHttpRequest != undefined) { // 这里不必 == 来推断,由于差别浏览器下效果不一样,safari 获得的是 'object',其他浏览器是'function'
    return function () {
      return new XMLHttpRequest()
    }
  } else if (typeof ActiveXObject != undefined) {
    var versions = [
      'MSXML2.XMLHttp.6.0', 'MSXML2.XMLHttp.3.0', 'MSXML2.XMLHttp'
    ]
    for (var i = 0, len = versions.length; i < len; i++) {
      try {
        new ActiveXObject(versions[i]);
        return function () {
          return new ActiveXObject(versions[i]);
        }
        break;
      } catch (ex) {}
    }
  } else {
    throw new Error('no XHR object available')
  }
}())

4. 函数绑定

函数绑定指的是建立一个新函数,可以在特定的this环境中以指定参数挪用另一个函数。
如:

function bind (fn, context) {
  return function () {
    return fn.apply(context, arguments)
  }
}
this.name = 'wfc'
var obj = {
  name: 'abc',
  sayName: function () {
    console.log(this.name)
  }
}
obj.sayName() // 'abc'
var newSayName = bind(obj.sayName, this)
newSayName() // 'wfc'

es5 为一切函数定义了一个原生的 bind() 要领。
被绑定函数与一般函数比拟有更多的分销,它们须要更多的内存,同时也由于多重函数挪用轻微慢一点,所以最好只在必要时运用。
// 支撑的浏览器有:IE 9+、Chrome、Firefox 4+、Safari 5.1+、Opera 11.6+

5. 原生bind() 要领

bind()要领会建立一个新函数。当这个新函数被挪用时,bind()的第一个参数将作为它运行时的 this, 以后的一序列参数将会在通报的实参前传入作为它的参数。
语法: fun.bind(thisArg[, arg1[, arg2[, …]]])
如:

this.x = 9;
var module = {
  x: 81,
  getX: function() { return this.x; }
};

module.getX(); // 返回 81

var retrieveX = module.getX;
retrieveX(); // 返回 9, 在这类情况下,"this"指向全局作用域

// 建立一个新函数,将"this"绑定到module对象
// 新手能够会被全局的x变量和module里的属性x所疑惑
var boundGetX = retrieveX.bind(module);
boundGetX(); // 返回 81

偏函数(Partial Functions)
bind()的另一个最简朴的用法是使一个函数具有预设的初始参数。
如:

function list() {
  return Array.prototype.slice.call(arguments);
}

var list1 = list(1, 2, 3); // [1, 2, 3]

// Create a function with a preset leading argument
var leadingThirtysevenList = list.bind(undefined, 37);

var list2 = leadingThirtysevenList(); // [37]
var list3 = leadingThirtysevenList(1, 2, 3); // [37, 1, 2, 3]

在默许情况下,运用 window.setTimeout() 时,this 关键字会指向 window (或全局)对象。
当运用类的要领时,须要 this 援用类的实例,你能够须要显式地把 this 绑定到回调函数以便继承运用实例。

function LateBloomer() {
  this.petalCount = Math.ceil(Math.random() * 12) + 1;
}

// Declare bloom after a delay of 1 second
LateBloomer.prototype.bloom = function() {
  window.setTimeout(this.declare.bind(this), 1000);
};

LateBloomer.prototype.declare = function() {
  console.log('I am a beautiful flower with ' +
    this.petalCount + ' petals!');
};

var flower = new LateBloomer();
flower.bloom();  // 一秒钟后, 挪用'declare'要领

6. 函数柯里化 (function currying)

函数柯里化 (function currying),用于建立已设置好了一个或许多个参数的函数。
如:

function curry(fn) {
  var args = Array.prototype.slice.call(arguments, 1)
  return function () {
    var newArgs = args.concat(Array.prototype.slice.call(arguments))
    fn.apply(undefined, newArgs)
  }
}

或许

function curry(fn, context) {
  var args = Array.prototype.slice.call(arguments, 2)
  return function () {
    var newArgs = args.concat(Array.prototype.slice.call(arguments))
    fn.apply(context, newArgs)
  }
}

es5 的 bind() 要领可以完成函数柯里化。详细见上一条。
须要注重的是 bind() curry() 不该滥用,由于每一个函数都邑带来分外的开支。

7. 不可扩大对象。

默许情况下,一切对象都是可以扩大的。
Object.preventExtensions(obj) 可以阻挠给对象增加新的属性和要领。然则已有的属性和要领不受任何影响,可以被修正和删除。
Object.isExtensible() 可以肯定对象是不是可以扩大。
如:

var obj = {
  name: 'wfc'
}
Object.isExtensible(obj) // true
Object.preventExtensions(obj)
obj.age = 18
obj.age // undefined
Object.isExtensible(obj) // false
obj.name = 'ming'
obj.name // 'ming'
delete obj.name // true
obj.name // undefined

8. 密封的对象。

es5 为对象定义的第二个庇护级别是密封对象。
密封对象不可扩大,而且一切成员的 [[Configurable]] 特征被设置成false,这意味着不能删除对象,然则属性值是可以修正的。
密封对象的要领是 Object.seal() 检测对象是不是被密封的要领是 Object.isSealed() 一切密封对象用 Object.isExtensible() 检测都是false。
如:

var person = {
  name: 'gang'
}
Object.isSealed(person) // false
Object.seal(person)
Object.isSealed(person) // true
Object.isExtensible(person) // false
person.name = 'wang'
person.name // 'wang'
delete person.name // false
person.name // 'wang'

然则可以赋值成 null 或许 undefined
如:

person.name = null
person.name // null
person.name = undefined
person.name // undefined

9. 凝结的对象

最严厉的防改动级别是凝结。凝结的对象既不可扩大,又是密封的,而且对象数据属性 [[Writable]] 特征会被设置成false。
凝结对象的要领是 Object.freeze()
检测对象是不是凝结的要领是 Object.isFrozen()
如:

var person = {
  name: 'wang'
}
Object.freeze(person)
Object.isFrozen(person) // true
delete person.name // false
person.name // 'wang'
person.name = 'gang'
person.name // 'wang'

对 JS 库的作者而言,凝结对象是很有效的。由于 JS 库最怕有人不测(或故意)的修正中心对象。

10. 定时器

关于 setTimeout() 现实实行时候大于即是设定时候。
关于 setInterval() 两次实行的现实距离时候小于即是设定时候。

11. 函数轮回支解

关于如许的轮回

for (var i = 0, len = arr.length; i < len; i++) {
  process(arr[i])
}

假如完成 process() 的时候较长,那末可以做以下支解。

function chunk (arr, process, context) {
  setTimeout(function () {
    var item = arr.shift()
    process.call(context, item)

    if (arr.length > 0) {
      setTimeout(arguments.callee, 100)
    }
  }, 100)
}

一旦某个函数须要花 50ms 以上来处置惩罚,就应该看看可否支解开来处置惩罚了。

12. 函数撙节。

浏览器中某些盘算和处置惩罚要比其他高贵许多,比方,DOM 操纵比非 DOM 交互须要更多的内存和CPU时候。
函数撙节背地的基本思想是:某些代码不可以在没有中断的情况下一连反复实行。
比方一连反复实行的 resize 事宜。
以下是该形式的基本形式:

var processor = {
  timeoutId: null,
  performProcessor: function () {
    handleProcess()
  },
  process: function () {
    clearTimeout(this.timeoutId)

    var that = this
    this.timeoutId = setTimeout(function () {
      that.performProcessor()
    }, 100)
  }
}

或许运用函数

function throttle (method, context) {
  clearTimeout(method.tid)
  method.tid = setTimeout(function () {
    method.call(context)
  }, 100)
}

只需代码是周期性实行的,都应该运用撙节,然则你不能掌握要求实行的速度。这里 throttle() 函数用 100 ms做距离,可以依据需求来变动。

13. 自定义事宜

定义:

function EventTarget () {
  this.handlers = {}
}

EventTarget.prototype = {
  constructor: EventTarget,
  addHandler: function (type, handler) {
    if (typeof this.handlers[type] === 'undefined') {
      this.handlers[type] = []
    }
    this.handlers[type].push(handler)
  },
  fire: function (event) {
    if (!event.target) {
      event.target = this
    }
    if (this.handlers[event.type] instanceof Array) {
      var handlers = this.handlers[event.type]
      for (var i = 0, len = handlers.length; i < len; i++) {
        handlers[i](event)
      }
    }
  },
  removeHandler: function (type, handler) {
    if (this.handlers[type] instanceof Array) {
      var handlers = this.handlers[type]
      for (var i = 0, len = handlers.length; i < len; i++) {
        if (handlers[i] === handler) {
          handlers.splice(i, 1)
          break
        }
      }
    }
  }
}

运用:

function handleMessage (event) {
  console.log(event.message)
}
var target = new EventTarget()
target.addHandler('message', handleMessage)
target.fire({
  type: 'message',
  message: 'Hello'
}) // 'hello'
target.removeHandler('message', handleMessage)
target.fire({
  type: 'message',
  message: 'Hello'
}) // 'undefined'

别的一种用法:

function Person (name, age) {
  EventTarget.call(this)
  this.name = name
  this.age = age
}
Person.prototype = Object.create(EventTarget.prototype)

Person.prototype.say = function (message) {
  this.fire({
    type: 'message',
    message: message
  })
}
var li = new Person('li', 18)
li.addHandler('message', handleMessage)
li.say('hahaha') // 'hahaha'

当代码中存在多个部分在特定时候互相交互的情况下,自定义事宜就异常有效了。
运用自定义事宜有助于结藕相干对象,坚持功用的阻隔。

关于《JavaScript高等程序设计(第三版)》这本书,我就选择性的看了一部分章节,所以,现在的择要笔记总结已悉数完毕。
    原文作者:wfc_666
    原文地址: https://segmentfault.com/a/1190000008109412
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞