> For the complete documentation index, see [llms.txt](https://note.niefee.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://note.niefee.com/summary/4/2016-nian-4-yue-20-ri.md).

# 2016年4月20日

\[TOC]

## 2016年4月20日

### javascript

#### apply()与call()

函数属性的继承与传递

```javascript
    /*定义一个人类*/
    function Person(name,age)
    {
        this.name=name;
        this.age=age;
    }
    /*定义一个学生类*/
    function Student(name,age,grade)
    {
        Person.apply(this,arguments);
        this.grade=grade;
    }
    //创建一个学生类
    var student=new Student("qian",21,"一年级");
    //测试
    alert("name:"+student.name+"\n"+"age:"+student.age+"\n"+"grade:"+student.grade);
    //大家可以看到测试结果name:qian  age:21  grade:一年级
    //学生类里面我没有给name和age属性赋值啊,为什么又存在这两个属性的值呢,这个就是apply的神奇之处.
```

**Function.apply(obj,args)**&#x65B9;法能接收两个参数 **obj**：这个对象将代替Function类里this对象,将调用函数**Person**里的**this**变成函数**Student**的**this**。 **args**：这个是数组，它将作为参数传给Function（args-->arguments）

> 在Studen函数里面可以将apply中修改成如下:
>
> > Person.call(this,name,age);

#### 函数的声明

* function命令

```javascript
function print(s) {
  console.log(s);
}
```

> 上面的代码命名了一个print函数，以后使用print()这种形式，就可以调用相应的代码。这叫做函数的声明（Function Declaration）。

* 函数表达式

```javascript
var print = function(s) {
  console.log(s);
};
```

> 这种写法将一个匿名函数赋值给变量。这时，这个匿名函数又称函数表达式（Function Expression），因为赋值语句的等号右侧只能放表达式。

```
var f = function f() {};
```

> 采用函数表达式声明函数时，function命令后面不带有函数名。如果加上函数名，该函数名只在函数体内部有效，在函数体外部无效。

* Function构造函数

```javascript
 var add = new Function(
  'x',
  'y',
  'return (x + y)'
);

// 等同于

function add(x, y) {
  return (x + y);
}
```

> 在上面代码中，Function构造函数接受三个参数，除了最后一个参数是add函数的“函数体”，其他参数都是add函数的参数。

#### 函数内部的变量提升

var命令声明的变量，不管在什么位置，变量声明都会被提升到函数体的头部。

```javascript
function foo(x) {
  if (x > 100) {
    var tmp = x - 100;
  }
}
```

上面的代码等同于

```javascript
function foo(x) {
  var tmp;
  if (x > 100) {
    tmp = x - 100;
  };
}
```

#### 函数本身的作用域

函数本身也是一个值，也有自己的作用域。它的作用域绑定其声明时所在的作用域。

```javascript
var a = 1;
var x = function () {
  console.log(a);
};

function f() {
  var a = 2;
  x();
}

f() // 1
```

函数x是在函数f的外部声明的，所以它的作用域绑定外层，内部变量a不会到函数f体内取值，所以输出1，而不是2。

#### 函数值传递方式

函数参数如果是原始类型的值（数值、字符串、布尔值），传递方式是传值传递（passes by value）。这意味着，在函数体内修改参数值，不会影响到函数外部。

```javascript
var p = 2;

function f(p) {
  p = 3;
}
f(p);

p // 2
```

如果函数参数是复合类型的值（数组、对象、其他函数），传递方式是传址传递（pass by reference）。也就是说，传入函数的原始值的地址，因此在函数内部修改参数，将会影响到原始值。

```javascript
var obj = {p: 1};

function f(o) {
  o.p = 2;
}
f(obj);

obj.p // 2
```

如果函数内部修改的，不是参数对象的某个属性，而是替换掉整个参数，这时不会影响到原始值。

```javascript
var obj = [1, 2, 3];

function f(o){
  o = [2, 3, 4];
}
f(obj);

obj // [1, 2, 3]
```

如果需要对某个原始类型的变量，获取传址传递的效果，可以将它写成全局对象的属性。

```
function f(p) {  window[p] = 2;}
```

#### arguments对象

虽然arguments很像数组，但它是一个对象。

下面是两种常用的转换方法：slice方法和逐一填入新数组。

```javascript
var args = Array.prototype.slice.call(arguments);

// or

var args = [];
for (var i = 0; i < arguments.length; i++) {
  args.push(arguments[i]);
}
```

**callee属性**

arguments对象带有一个callee属性，返回它所对应的原函数。

```javascript
var f = function(one) {
  console.log(arguments.callee === f);
}

f() // true
```

#### 闭包

函数内的函数。

```javascript
function f1() {
  var n = 999;
  function f2() {
    console.log(n);
  }
  return f2;
}

var result = f1();
result(); // 999
```

JavaScript语言特有的”链式作用域”结构（chain scope），子对象会一级一级地向上寻找所有父对象的变量。

闭包的最大用处有两个，一个是可以读取函数内部的变量，另一个就是让这些变量始终保持在内存中，即闭包可以使得它诞生环境一直存在。

```javascript
function createIncrementor(start) {
  return function () {
    return start++;
  };
}

var inc = createIncrementor(5);

inc() // 5
inc() // 6
inc() // 7
```

外层函数每次运行，都会生成一个新的闭包，而这个闭包又会保留外层函数的内部变量，所以内存消耗很大。因此不能滥用闭包，否则会造成网页的性能问题。

#### 立即调用的函数表达式（IIFE）

```javascript
(function(){ /* code */ }()); 

// 或者

(function(){ /* code */ })();
```

让引擎知道，圆括号前面的部分不是函数定义语句，而是一个表达式，这样就可以对此进行运算。

```javascript
var i = function(){ return 10; }();

true && function(){ /* code */ }();

0, function(){ /* code */ }();
!function(){ /* code */ }();

~function(){ /* code */ }();

-function(){ /* code */ }();

+function(){ /* code */ }();

new function(){ /* code */ }

new function(){ /* code */ }() // 只有传递参数时，才需要最后那个圆括号。
```

一是不必为函数命名，避免了污染全局变量；二是IIFE内部形成了一个单独的作用域，可以封装一些外部无法读取的私有变量。

#### eval命令

eval命令的作用是，将字符串当作语句执行。

```javascript
eval('var a = 1;');

a // 1
```
