Javascript 特性介绍

Posted on July 31, 2014

1. 函数是一等对象(first-class object)

  • 函数可以在运行时动态创建,还可以在程序执行过程中创建
  • 函数可以分配变量,可以将它们的引用复制到其他变量
  • 可以作为参数传递给其他函数,可以作为返回值(高阶函数)
  • 函数可以有自己的属性和方法

简单的说,一等对象就是说在使用上和其他对象没有任何区别

// 函数作为返回值
function foo(x) {
    return function() {
        return x;
    }
}

// 函数作为参数
function foo(x, bar) {
    return bar(x);
}
foo('javascript', function(args) { console.log('hello ' + args) }) //hello javascript

类比ruby 中的可调用对象:Proc对象, lambda, method对象

bar = ->() { puts 'Im a lambda object' }

# lambda作为返回值
def foo
    ->() { puts 'Im a lambda object' }
end

# lambda作为参数
def foo(x, bar)
    bar.call(x)
end
foo('ruby', ->(args) { puts "hello #{args}" }) #hello ruby

2. 闭包

定义: 一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数)

闭包的实现基础:

  • 函数是一等对象(可以传递, 可以用变量引用)
  • 函数提供了作用域
var m = 'hello '
function foo() {
    var n = 'javascript';
    return function () {
        console.log(m + n);
    };
}
var bar = foo();
bar(); //hello javascript

类比ruby中的method和lambda:

m = 'hello '
def foo
  n = 'ruby';
  return -> { puts n }
  #return -> { puts m } #undefined local variable or method `m'
end

bar = foo()
bar.call # ruby

ruby 中的binding:

binding 代表了代码执行的上下文环境(类似javascript中的闭包)

  • Kernel#binding可以获得当前的Binding对象

  • Proc#binding可以获得代码块的闭包Binding对象

    # 例1
    def var_from_binding(&b)
      eval('var', b.binding)
    end
    var = 123
    var_from_binding {} #123
    
    # 例2
    class A
      def abc
        binding
      end
    
      private
      def xyz
        'xyz'
      end
    end
    
    eval "xyz",  A.new.abc # => "xyz"
    

总结:

  • ruby 中有且只有三个作用域门 def class module 开启了全新的作用域, lambda 开启了一个扁平作用域(flattening)

  • javascript中的function 开启的是扁平作用域

  • javascript 中的闭包类似ruby中的binding


3. 原型链继承

function Animal() {
  this.color = 'unknown';
}

Animal.prototype.say = function() { console.log('Im a animal with color ' + this.color); };
Animal.prototype.eat = function() { console.log('eat food'); };

function Duck() {
  this.color = 'yellow';
}

Duck.prototype = new Animal();      //继承
Duck.prototype.constructor = Duck;  //对constructor进行修正

Duck.prototype.eat = function() { console.log('eat fish'); };

animal = new Animal();
animal.say();   //Im a animal with color unknown
animal.eat();   //eat food

duck = new Duck();
duck.say();     //Im a animal with color yellow
duck.eat();     //eat fish

类比ruby代码(不是完全等价):

class Animal
  attr_accessor :color

  def initialize
    self.color = 'unknown'
  end

  def say
    puts "Im a animal with color #{self.color}"
  end

  def eat
    puts 'eat food'
  end

end

class Duck < Animal  #继承

  def initialize
    self.color = 'yellow'
  end

  def eat
    puts 'eat fish'
  end

end

animal = Animal.new
animal.say   #Im a animal with color unknown
animal.eat   #eat food

duck = Duck.new;
duck.say     #Im a animal with color yellow
duck.eat     #eat fish

其他:

  • javascript 中的每个function都有prototype属性
  • 对function 的实例, 对prototype中的属性有读权限, 但是不能写(如果写的话, 会修改/添加自身属性)
  • 在构造器function中的this表示该构造器新建的实例, 该this会被自动return
  • 在构造器中, 对this增加的属性是普通属性, 可以读写(区别于prototype中的属性)
  • 对象的方法hasOwnProperty 用于过滤原型链上的属性