2016年4月20日

[TOC]

2016年4月20日

javascript

apply()与call()

函数属性的继承与传递

    /*定义一个人类*/
    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)方法能接收两个参数 obj:这个对象将代替Function类里this对象,将调用函数Person里的this变成函数Studentthisargs:这个是数组,它将作为参数传给Function(args-->arguments)

在Studen函数里面可以将apply中修改成如下:

Person.call(this,name,age);

函数的声明

  • function命令

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

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

  • 函数表达式

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

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

var f = function f() {};

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

  • Function构造函数

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

// 等同于

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

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

函数内部的变量提升

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

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

上面的代码等同于

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

函数本身的作用域

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

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)。这意味着,在函数体内修改参数值,不会影响到函数外部。

var p = 2;

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

p // 2

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

var obj = {p: 1};

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

obj.p // 2

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

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方法和逐一填入新数组。

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属性,返回它所对应的原函数。

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

f() // true

闭包

函数内的函数。

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

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

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

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

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

var inc = createIncrementor(5);

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

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

立即调用的函数表达式(IIFE)

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

// 或者

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

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

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命令的作用是,将字符串当作语句执行。

eval('var a = 1;');

a // 1

Last updated