# 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
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://note.niefee.com/summary/4/2016-nian-4-yue-20-ri.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
