[关闭]
@demonly 2017-10-14T08:57:55.000000Z 字数 2603 阅读 1246

Function

JavaScript


函数属性和方法

函数包含两个属性:length 和prototype。length 表示函数希望接收的命名参数的个数。每个函数还包含一些方法,apply()、call()和 bind()。

apply()方法接收两个参数:一个是在其中运行函数的作用域,另一个是由参数组成的数组。

call()方法接收任意个参数,第一个是运行函数的作用域,其余的是传给函数的参数。

bind()方法创建一个函数的实例,其 this 值会被绑定到传给 bind()函数的值。

  1. var objectSayColor = sayColor.bind(o);

闭包

闭包常见方式就是在函数内部声明函数,这样后者就可以自由地访问前者作用域的变量,即使该函数是在其他地方被调用。

  1. var Person = (function(){
  2. var n = 1;
  3. function Person(){
  4. console.log(n)
  5. }
  6. return Person;
  7. })();
  8. Person();

其原理是将外部函数中的变量保存在[[scope]]属性中并加入到作用域链中,作用域链是一个指向对象的指针列表,只引用对象不包含实际对象。当内部函数被调用时会首先复制[[scope]]属性中的对象并创建活动对象,构建起执行环境再运行。
Closure.jpg-59.7kB

一般的函数在执行完毕后会销毁活动对象。但是在闭包的情况下,外部函数执行完毕后,由于有内部函数仍然在引用其活动对象,因此活动对象不会被销毁。这种活动对象可以通过设置其等于 null 来手动销毁。

关于 this 对象

当函数作为某个对象的方法调用时,this 指向该对象。
在全局函数中 this 指向 window。
匿名函数中 this 指向 window。(当使用 apply 或者 call 方法改变执行环境时例外)

  1. var name = 'The Window'
  2. >var object = {
  3.   name : 'My Object',
  4.   getNameFunc : function () {
  5.     return function () {
  6.       return this.name;
  7.     };
  8.   }
  9. };
  10. alert(object.getNameFunc()() );  //'The Window'

其原因就是 this 包含在一个匿名函数中,因此其指向的是 window,即全局环境。这种情况只需将 this 写在外部函数中并保存在对象中,通过作用域链传入内部函数。
下面看几种特殊情况。

  1. var name = 'The Window'
  2. var object = {
  3.   name : 'My Object',
  4.   getNameFunc : function() {
  5.     return this.name;
  6.   }
  7. };
  8. object.getName();  //'My Object'
  9. (object.getName)();  //'My Object'
  10. (object.getName = object.getName)();  //'The Window'

前两条代码的意思都是一样的,在 object 下执行 getName()。第三条是先赋值再调用赋值后的结果,表面上看像是在 object 下执行,其实是在全局环境下执行的。

内存泄漏

当外部函数引用内部函数时就会产生循环引用,循环引用会导致变量长时间占用内存。由于作用域链的原因,即使内部函数没有引用外部函数也会产生循环引用,但是这种情况通常会被 javascript 检测到并且自动清除(IE8及以下不会)。

但是如果在一个引用循环中包含了一个 DOM 元素,那么这个循环在旧 IE 中将永远无法被释放,除非关闭浏览器,随着时间的推移这类循环可能不断积累。

导致这种循环的常见原因是在外部函数中为 DOM 元素绑定事件的同时定义函数。这样定义的函数就会通过作用域链引用 DOM 元素,而 DOM 元素则通过事件引用内部函数形成循环引用。避免这种情况的方法就是在全局下定义函数而不是在外部函数中。也可以使用 jquery 来绑定方法,jquery 会手动释放这个内存。
另一种情况是在函数中引用了 DOM 元素的某项属性,这种情况只需要在外部函数中将属性保存在另外的对象中再引用即可。

模仿块级作用域

块级作用域(私有作用域)用于将代码隔离出来避免多个代码块之间的变量产生冲突。

  1. (function() {
  2.   //一段代码
  3. })();

函数声明部分用括号包起来的原因是 JavaScript 中不允许在函数声明之后接括号。

ES6

参数默认值

  1. function add(first, second = first) {
  2. return first + second;
  3. }

默认参数在每一次调用时求值

不定参数

  1. function pick(object, ...keys) {
  2. }

不定参数只能是最后一个参数

展开运算符

使用展开运算符可以将数组展开作为参数传入

  1. let values = [-25, -50, -75, -100]
  2. Math.max(...values, 0);

箭头函数

  1. // 基本语法
  2. let sum = (value) => {
  3. return value;
  4. }
  5. // 只有一个参数时可以直接写参数名,简单返回时可以不用写大括号
  6. let reflect = value => value;

Iterator 和 Generator

迭代器是一类特殊对象,每个迭代器对象都有一个 next() 方法,每次调用都返回一个结果对象,结果对象有两个属性一个是 value,另一个是 done。当没有更多可返回数据时结果对象的 done 属性为 true,value 属性为 Iterator 函数的返回值。

生成器是一种返回迭代器的函数,通过 function 关键字后或者函数名前加 * 来表示,在 generator 函数中可以使用 yield 关键字。
返回的迭代器在第一次调用 next() 方法时开始执行,遇到 yield 关键字时停止,并将 yield 关键字后的值作为 value 返回。
调用 next 时可以传入一个参数,这个参数作为该 yield 表达式的值。

  1. function *createIterator (){
  2. console.log(1);
  3. let n = yield 1;
  4. console.log(n);
  5. yield 2;
  6. }
  7. let iterator = createIterator();
  8. iterator.next()
  9. // 1
  10. // <- {value: 1, done: false}
  11. iterator.next(2)
  12. // 2
  13. // <- {value: 2, done: false}
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注