我是世界第一等:函数

  1995年5月,美国程序员Brendan Eich只用了10天,完成了javascript的设计,函数是这门语言的第一等公民。一经推出,网景公司迅速统治了整个浏览器市场。

  1997年12月,中国歌手刘德华表示不服,于是写了一首用闽南语唱的歌,叫做《世界第一等》。可惜的是,刘德华现在还是连个Hello World都不会写。

  

  心疼华仔……

 

  不过还是要回到主题,为啥我们常说,函数是javascript世界的第一等呢?

  一般的编程语言,第一类对象都具备如下几个特点:

  1. 可以通过字面量创建
  2. 可被赋于变量、数组元素和其他对象的属性
  3. 可以作为参数传递给函数
  4. 可以作为函数的返回值
  5. 可以含有能被动态创建和赋值的属性

  (简单来讲,当你能够传递、返回、赋值一个类型时,该类型通常被视为”一级公民“)

  JavaScript的函数拥有所有以上能力,能像其他对象一样使用(。因此,我们说函数是第一类对象。下面是简单演示:

 

    function civics1(){
        //可以通过字面量创建
    }
    
    
    var foo = {"value":'bar'};
    civic2 = foo;//被赋于变量
    civics1 = foo.value;//被赋于对象的属性
    civics1 = [1,2,3];//被赋于数组

    
    function civics2(){
        console.log("Yeah");
    }
    function callback(fn){
        console.log("I am the first!");
        fn();//有时候会使用call或者apply
    }
    callback(civics2);//作为参数传递

    
    function closure(){
        var free = '';
        return function(){
            return free + 1;
        };//做为返回值
    }

    
    civics2.attr = 'created';//属性可以被赋值
    console.log(civics2.attr);

  可以看到,函数除了具备对象的所有功能,它区别对象之处是能被调用。

  函数有三种:普通函数,内联函数和匿名函数。

  函数的调用方式有四种:

  1. 直接调用
  2. 作为对象的方法调用
  3. 作为对象的构造函数调用
  4. 通过 apply( ) 或 call( ) 调用

  调用 JavaScript 函数时如果形参(parameter)和实参(augment)的数目对不上,不会报错。

  • 如果是实参多于形参,多出来的部分被忽略掉。
  • 如果是形参多于实参,没被赋值的会被设为 undefined。

  所有的函数调用都会有两个隐含的形参:arguments 和 this

  • arguments: 神似数组但不是数组。它有 length 属性,得到arguments 的长度,也可以用 index,例如 arguments[0] 访问第一个元素,但就只有这些了,没有数组具有的其他方法。
  • this:函数上下文(function context),具体是啥得看怎么被调用的

  函数的第一种和第二种调用的方式 (直接调用和作为对象方法调用)其实是一样的。因为在浏览器里,第一种其实就是 window 对象的方法,如果函数是全局函数的话。

  说到第三种,顺便引申一下,ECMAScript 2015(ES6)规范已经支持class(类)了,并且还支持extend(继承),class根据 constructor 方法来创建和初始化对象,MDN的例子如下:

 

class Square extends Polygon {
  constructor(length) {
    // Here, it calls the parent class' constructor with lengths
    // provided for the Polygon's width and height
    super(length, length);
    // Note: In derived classes, super() must be called before you
    // can use 'this'. Leaving this out will cause a reference error.
    this.name = 'Square';
  }

  get area() {
    return this.height * this.width;
  }

  set area(value) {
    this.area = value;
  } 
}

 

  毕竟主流浏览器还不兼容,所以再回到ES5吧:通过 new 关键字来调用构造函数,就像这样:

    function MyFunc(name,sex){
        this.name = name;
        this.sex = sex;
    }
    var func = new MyFunc('uncle','male');
    console.log(func.name +'--'+func.sex);//uncle--male

 

  使用构造函数调用后,以下会发生:

  • 一个新的对象被创建
  • 这个对象作为 this 参数被传递给构造函数,变成这个构造函数的函数上下文
  • 没有显式返回值,这个新对象就作为这个构造函数的值被返回

  new的那一瞬间,做了这四件事:

    var obj  ={};//创建空对象
    obj.__proto__ = MyFunc.prototype;//将这个空对象的__proto__成员指向了构造函数对象的prototype成员对象
    MyFunc.call(obj);//将构造函数的作用域赋给新对
    return obj//返回新对象obj

 

  第二行是最重要的一步,MyFunc.prototype有个构造器,这个构造器就是函数本身:

   所以通过自定义prototype.constructor,并不能改变初始化,因为创建他们的是只读的原生构造函数(native constructors):

    function MyFunc(name,sex){
        this.name = name;
        this.sex = sex;
    }
    MyFunc.prototype.constructor = function(){
        this.name = 'father';
    };//重新定义构造器
    var func = new MyFunc('uncle','male');
    console.log(func.name + '--' + func.sex);//uncle--male

 

 

  最后一种,apply( )call( ) ,这又涉及到更多的知识点,这次不作具体展开。

posted @ 2016-06-02 10:09  肖大叔的小巫  阅读(562)  评论(0编辑  收藏  举报