《你不知道的JavaScript(上)》第二部分-this和对象原型

107 阅读12分钟

第1章 关于this

1.1 为什么要用this?

对比一下使用this和不使用this的区别?

显式的传递上下文:

function identify(context){
  return context.name.toUpperCase()
}
funcion speak(context){
  var greeting = "Hello, I'm" + identify(context)
  
  console.log(greeting)
}
var me = {
  name: "Kyle"
}
var you = {
  name: "Reader"
}
identify(you)
speak(me)

使用this,this提供了一种更优雅的方式来隐式"传递"一个对象引用:

function identify(){
  return this.name.toUpperCase()
}
function speak(){
  var greeting = "Hello, I'm" + identify.call(this)
  
  console.log(greeting)
}
var me = {
  name: "Kyle"
}
var you = {
  name: "Reader"
}
identify.call(me)
identify.call(you)

speak.call(me)
speak.call(you)

1.2 误解

1.2.1 指向自身

function foo(num){
  console.log("foo:" + num)
  
  this.count++
}
foo.count = 0

var i;
for(i = 0; i < 10; i++){
  if(i > 5){
    foo(i)
  }
}

console.log(foo.count)

上面的🌰输出什么?怎么解决呢?

  1. 使用foo标识符代替this来引用函数对象。
  2. 强制this指向foo函数对象。

1.2.2 它的作用域

function foo(){
  var a = 2
  this.bar()
}
function bar(){
  console.log(this.a)
}
foo()

上面的🌰输出什么?

this实际上是在函数被调用时发生的绑定,它指向什么完全取决于---。

第2章 this的全面解析

2.1 调用位置

看看什么是调用栈和调用位置:

function baz(){
  console.log("baz")
  bar()
}
function bar(){
  console.log("bar")
  foo()
}
function foo(){
  console.log("foo")
}
baz()

2.2 绑定规则

2.2.1 默认绑定

function foo(){
  console.log(this.a)
}

var a = 2
foo()

非严格模式下,上面的🌰输出什么?

function foo(){
  "use strict"
  
  console.log(this.a)
}
var a = 2

foo()

上面的🌰输出什么?

function foo(){
  console.log(this.a)
}
var a = 2
(function(){
  'use strict'
  
  foo()
})()

上面的🌰输出什么?

2.2.2 隐式绑定

function foo(){
  console.log(this.a)
}
var obj = {
  a: 2,
  foo: foo
}
obj.foo()

上面的🌰输出什么?

function foo(){
  console.log(this.a)
}
var obj2 = {
  a: 42,
  foo: foo
}
var obj1 = {
  a: 2,
  obj2: obj2
}
obj1.obj2.foo()

上面的🌰输出什么?

function foo(){
  console.log(this.a)
}
var obj = {
  a: 2, 
  foo: foo
}
var bar = obj.foo
var a = "oops, global"
bar()

上面的🌰输出什么?

function foo(){
  console.log(this.a)
}

function doFoo(fn){
  fn()
}
var obj = {
  a: 2,
  foo: foo
}
var a = "oops, global"
doFoo(obj.foo)

上面的🌰输出什么?

function foo(){
  console.log(this.a)
}
var obj = {
  a: 2, 
  foo: foo
}
var a = "oops, global"
setTimeout(obj.foo, 100)

上面的🌰输出什么?

2.2.3 显式绑定

function foo(){
  console.log(this.a)
}
var obj = {
  a: 2
}
foo.call(obj)

1.硬绑定

function foo(){
  console.log(this.a)
}
var obj = {
  a: 2
}
var bar = function(){
  foo.call(obj)
}
bar()
setTimeout(bar, 100)

bar.call(window)

上面的🌰输出什么?

function foo(something){
  console.log(this.a, something)
  return this.a + something
}
var obj = {
  a: 2
}
var bar = function(){
  return foo.apply(obj, arguments)
}
var b = bar(3)
console.log(b)

上面的🌰输出什么?

function foo(something){
  console.log(this.a, something)
  return this.a + something
}
function bind(fn, obj){
  return function(){
    return fn.apply(obj, arguments)
  }
}
var obj = {
  a: 2
}
var bar = bind(foo, obj)
var b = bar(3)
console.log(b)

上面的🌰输出什么?

function foo(something){
  console.log(this.a, something)
  return this.a + something
}
var obj = {
  a: 2
}
var bar = foo.bind(obj)
var b = bar(3)
console.log(b)

上面的🌰输出什么?

2. API 调用的"上下文"

function foo(el){
  console.log(el, this.id)
}
var obj = {
  id: "awesome"
}
[1,2,3].forEach(foo, obj)

上面的🌰输出什么?

2.2.4 new绑定

2.3 优先级

function foo(){
  console.log(this.a)
}
var obj1 = {
  a: 2, 
  foo: foo
}

var obj2 = {
  a: 3, 
  foo: foo
}
obj1.foo()
obj2.foo()

obj1.foo.call(obj2)
obj2.foo.call(obj1)

上面的🌰输出什么?

隐式绑定和显式绑定哪个优先级更高?

function foo(something){
  this.a = something
}
var obj1 = {
   foo: foo
}
var obj2 = {}

obj1.foo(2)
console.log(obj1.a)

obj1.foo.call(obj2, 3)
console.log(obj2.a)

var bar = new obj1.foo(4)
console.log(obj1.a)
console.log(bar.a)

上面的🌰输出什么?

new绑定和隐式绑定哪个优先级更高?

function foo(something){
  this.a = something
}
var obj1 = {}
var bar = foo.bind(obj1)
bar(2)
console.log(obj1.a)

var baz = new bar(3)
console.log(obj1.a)
console.log(baz.a)

上面的🌰输出什么?

new绑定和硬绑定哪个优先级更高?

bind(...)的功能之一就是可以把第一个参数之外的所有参数传给下层函数(是"柯里化"的一种)。

function foo(p1, p2){
  this.val = p1 + p2
}
var bar = foo.bind(null, "p1")

var baz = new bar("p2")

baz.val

上面的🌰输出什么?

2.4 绑定例外

2.4.1 被忽略的this

🌟

function foo(){
  console.log(this.a)
}

var a = 2
foo.call(null)

上面的🌰输出什么?

function foo(a, b){
  console.log("a:"+ a + ",b:" + b)
}

foo.apply(null, [2, 3])

var bar = foo.bind(null, 2)
bar(3)

上面的🌰输出什么?

2.4.2 间接引用

间接引用最容易在赋值时发生:

function foo(){
  console.log(this.a)
}
var a = 2
var o = {a: 3, foo: foo}
var p = {a: 4}

o.foo()
(p.foo = o.foo)()

2.4.3 软绑定

使用硬绑定之后就无法使用隐式绑定或显式绑定来修改this。

软绑定:如果可以给默认绑定指定一个全局对象和undefined以外的值,那就可以实现和硬绑定一样的效果,同时保留隐式绑定或显式绑定修改this的能力。

function foo() {
  console.log("name:" + this.name);
}
var obj = { name: "obj" },
  obj2 = { name: "obj2" },
  obj3 = { name: "obj3" };

var fooOBJ = foo.softBind(obj);

fooOBJ();
obj2.foo = foo.softBind(obj);
obj2.foo();

fooOBJ.call(obj3);

setTimeout(obj2.foo, 10);

软绑定还理解不了。

🌟2.5 this词法

箭头函数:根据外层(函数或者全局)作用域来决定this。

function foo(){
  return (a) => {
    console.log(this.a)
  }
}
var obj1 = {
  a: 2
}
var obj2 = {
  a: 3
}
var bar = foo.call(obj1)
bar.call(obj2)

上面的🌰输出什么?

function foo(){
  setTimeout(() => {
    console.log(this.a)
  }, 100)
}
var obj = {
  a: 2
}
foo.call(obj)

上面的🌰输出什么?

function foo(){
  var self = this;
  setTimeout(function(){
    console.log(self.a)
  }, 100)
}
var obj = {
  a: 2
}
foo.call(obj)

上面的🌰输出什么?

第3章 对象

3.1 语法

对象的文字语法:

var myObj = {
  key: value
  // ... 
}

构造形式大概是:

var myObj = new Object()
myObj.key = value

3.2 类型

六种简单基本类型?

typeof null 返回什么?

3.3 内容

属性访问:

var myObject = {
  a: 2
}
myObject.a

myObject["a"]

3.3.1 可计算属性名

var prefix = "foo"

var myObject = {
  [prefix + "bar"]: "hello",
  [prefix + "baz"]: "world" 
}

myObject["foobar"]
myObject["foobaz"]

3.3.2 属性与方法

无论返回值是什么类型,每次访问对象的属性就是属性访问。

function foo(){
  console.log("foo")
}
var someFoo = foo

var myObject = {
  someFoo: foo
}

foo;
someFoo;
myObject.someFoo

3.3.3 数组

var myArray = ["foo", 42, "bar"]

myArray.length;
myArray[0]
myArray[2]

上面的🌰输出什么?

var myArray = ["foo", 42, "bar"]

myArray.baz = "baz"
myArray.length
myArray.baz

上面的🌰输出什么?

var myArray = ["foo", 42, "bar"]

myArray["3"] = "baz"
myArray.length;
myArray[3]

上面的🌰输出什么?

3.3.4 复制对象

思考一下这个对象🌰:

function anotherFunction(){/*..*/}

var anotherObject = {
  c: true
}
var anotherArray = []

var myOject = {
  a: 2, 
  b: anotherObject,
  c: anotherArray,
  d: anotherFunction
}
anotherArray.push(anotherObject, myOject)
  1. 浅复制还是深复制?
  2. 深复制会有什么问题?怎么解决?
  3. ES6定义了Object.assign(...)方法是干嘛的?它怎么用?

3.3.5 属性描述符

ES5开始,所有的属性都具备了属性描述符。

var myObject = {
  a: 2
}
Object.getOwnPropertyDescriptor(myObject, "a")

上面的🌰输出什么?

var myObject = {}
Object.defineProperty(myObject, "a", {
  value: 2,
  writable: true,
  configurable: true,
  enumerable: true
})
myObject.a

上面的🌰输出什么?

1. Writable

var myObject = {}
Object.defineProperty(myObject, "a", {
  value: 2,
  writable: false,
  configurable: true,
  enumerable: true
})
myObject.a = 3
myObject.a

上面的🌰输出什么? 如果是在严格模式下呢?

"use strict"
var myObject = {}

Object.defineProperty(myObject, "a", {
  value: 2,
  writable: false,
  configurable: true,
  enumerable: true
})
myObject.a = 3

2. Configurable

属性是可配置的,就可以使用defineProperty(...)方法修改属性描述符:

var myObject = {
  a: 2
}
myObject.a = 3
myObject.a

Object.defineProperty(myObject, "a", {
  value: 4,
  writable: true,
  configurable: false
})

myObject.a
myObject.a = 5
myObject.a

Object.defineProperty(myObject, "a", {
  value: 6,
  writable: true,
  configurable: true,
  enumerable: true
})

上面的🌰输出什么?

var myObject = {
  a: 2
}
myObject.a

delete myObject.a
myObject.a

Object.defineProperty(myObject, "a", {
  value: 2,
  writable: true,
  configurable: false,
  enumerable: true
})
myObject.a
delete myObject.a
myObject.a

上面的🌰输出什么?

3. Enumerable

这个描述符怎么用?

3.3.6 不变性

1.对象常量

var myObject = {}

Object.definePropery(myObject, "FAVORITE NUMBER", {
  value: 42,
  writable: false,
  configurable: false
})

2.禁止扩展

var myObject = {
  a: 2
}
Object.preventExtensions(myObject)

myObject.b = 3
myObject.b

上面的🌰输出什么?

3.密封

密封之后会怎么样?

4.冻结

冻结之后会怎么样?

3.3.7 [[Get]]

var myObject = {
  a: 2
}
myObject.a

上面的🌰输出什么?

var myObject = {
  a: 2
}
myObject.b

上面的🌰输出什么?

如果是引用了一个当前词法作用域中不存在的变量,会输出什么?

3.3.8 [[Put]]

[[Put]]操作的步骤?

3.3.9 Getter和Setter

var myObject = {
  get a(){
    return 2
  }
}
Object.defineProperty(
  myObject,
  "b",
  {
    get: function(){ return this.a * 2},
    enumerable: true
  }
)

myObject.a;
myObject.b;

上面的🌰输出什么?

var myObject = {
  get a(){
    return 2
  }
}
myObject.a = 3
myObject.a

上面的🌰输出什么?

var myObject = {
  get a(){
    return this.a
  },
  set a(val){
    this.a = val * 2
  }
}
myObject.a = 2
myObject.a

上面的🌰输出什么?

3.3.10 存在性

var myObject = {
  a: 2
}
("a" in myObject)
("b" in myObject)

myObject.hasOwnProperty("a")
myObject.hasOwnProperty("b")

上面的🌰输出什么?

in操作符和hasOwnProperty(...)有什么区别?

4 in [2, 4, 6]

上面的🌰输出什么?

1.枚举

var myObject = {}

Object.defineProperty(
  myObject,
  "a",
  { 
    enumberable: true,
    value: 2
  }
)
Object.defineProperty(
  myObject,
  "b",
  { 
    enumberable: false,
    value: 3
  }
)

myObject.b;
("b" in myObject);
myObject.hasOwnProperty("b");

//.......

for(var k in myObject){
  console.log(k, myObject[k])
}

上面的🌰输出什么?

for...in可以用来遍历数组吗?

var myObject = {}

Object.defineProperty(
  myObject,
  "a",
  { 
    enumberable: true,
    value: 2
  }
)

Object.defineProperty(
  myObject,
  "b",
  { 
    enumberable: false,
    value: 3
  }
)

myObject.propertyIsEnumertable("a")
myObject.propertyIsEnumertable("b")

Object.keys(myObject)
Object.getOwnPropertyNames(myObject)

上面的🌰输出什么?

Object.keys(...)Object.getOwnPropertyNames(...)有什么区别?

遍历

var myArray = [1,2,3]

for(var i = 0; i < myArray.length; i++){
  console.log(myArray[i])
}

上面的🌰输出什么?

var myArray = [1, 2, 3]
for(var v of myArray){
  console.log(v)
}

上面的🌰输出什么?

var myArray = [1,2,3]
var it = myArray[Symobol.iterator]()

it.next();
it.next();
it.next();
it.next();

上面的🌰输出什么?

如何为想遍历的对象定义@@iterator?

var myObject = {
  a: 2,
  b: 3
}

// 实现@@iterator

var it = myObject[Symbol.iterator]()
it.next()
it.next()
it.next()

如何定义一个"无限"迭代器?它永远不会"结束"并且总会返回一个新值。

var random  = 
// 实现random迭代器

var random pool = []
for(var n of randoms){
  random pool.push(n)
  
  if(randoms pool.length === 100) break;
}

第4章

4.1 类理论

JavaScript的机制其实和类完全不同。

4.2 类的机制

4.2.1 建造

"类"和"实例"的概念来源于房屋建造。(好新奇,第一个听说这个比喻!一下子就通透了)。 建筑师只会去规划一个建筑蓝图。建筑工人会按照蓝图建造建筑。 一个类就是一张蓝图。为了获得真正可以交互的对象,我们必须按照类来建造(也可以说实例化)一个东西。

类通过复制操作被实例化为对象形式:

image.png

4.2.2 构造函数

class CoolGuy{
  specialTrick = nothing
  
  CoolGuy(trick){
   specialTrick = trick
  }
  
  showOff(){
    output("Here's my trick:", specialTrick)
  }
}

调用类构造函数来生成一个CoolGuy实例:

Joe = new CoolGuy("jumping rope")
Joe.showOff() // Here's my trick: jumping rope

4.3 类的继承

class Vehicle {
  engines = 1
  
  ignition(){
    output("Turning on my engine.")
  }
  
  drive(){
    ignition()
    output("Steerubg and moving forward! ")
  }
}

class Car inherits Vehicle {
  wheels = 4
  
  drive(){
    inherited: drive()
    output("Rolling on all", wheels, "wheels!")
  }
}

class SpeedBoat inherits Vehicle{
  engines = 2
  
  ignition(){
    output("Turning on my", engines, " engines.")
  }
  
  pilot(){
    inherited: drive()
    output("Speeding through the water with ease! ")
  }
}

4.3.1 多态

什么是多态

4.3.2 多重继承

image.png

子类D继承自两个父类(B和C),这两个父类都继承自A。如果A中有drive()方法并且B和C都重写了这个方法(多态),那当D引用drive()时应当选择哪个版本呢(B: drive()还是C:drive())?

4.4 混入

4.4.1 显式混入

// 实现mixin方法

var Vehicle = {
  engines: 1,
  ignition: function(){
    console.log("Turning on my engine.")
  },
  drive: function(){
    this.ignition()
    console.log("Steerging and moving forward!")
  }
}

var Car = mixin(Vehicle, {
  wheeks: 4,
  drive: function(){
    Vehicle.drive.call(this)
    console.log("Rolling on all " + this.wheels + "wheels! ")
  }
})

在JavaScript中不存在类Vehicle和Car都是对象,供我们分别进行复制和粘贴。

1. 再说多态

Vehicle.drive.call(this)就是显式多态

2. 混合复制

// 另一种混入函数,可能有重写风险

var Vehicle = {
  // ...
}

// 首先创建一个空对象并把Vehicle的内容复制进去

// 然后把新内容复制到Car中

Car就和Vehicle分离了,向Car中添加属性不会影响Vehicle。

3. 寄生继承


// "传统的JavaScript类"Vehicle
function Vehicle(){
  this.engines = 1
}
Vehicle.prototype.ignition = function(){
  console.log("Turning on my engine.")
}

Vehicle.prototype.drive = function(){
  this.ignition()
  console.log("Steering and moving forward!")
}

// 完成”寄生类“Car
function Car(){

}

var myCar = new Car()

myCar.drive()

4.4.2 隐式混入

var Something = {
  cool: function(){
    this.greeting = "Hello World"
    this.count = this.count ? this.count + 1 : 1
  }
}
Something.cool();
Something.greeting;
Something.count;

var Another = {
  cool: function(){
    // 隐式把Something混入Another
    Something.cool.call(this)
  }
}
Another.cool();
Another.greeting;
Another.count;

上面的🌰输出什么?

第5章 原型

5.1 [[Pototype]]

var myObject = {
  a: 2
}
myObject.a;

对于默认的[[Get]]操作来说,如果无法在对象本身找到需要的属性,就会访问对象的[[Prototype]]链。

var anotherObject = {
  a: 2
}
var myObject = Object.create(anotherObject)

myObject.a;

Object.create是做什么的?上面的🌰输出什么?

var anotherObject = {
  a: 2
}

var myObject = Object.create(anotherObject)

for(var k in myObject){
  console.log("found: " + k)
}

("a" in myObject);

上面的🌰输出什么?

5.1.2 属性设置和屏蔽

myObject.foo = "bar"
  1. 如果myObject对象中包含foo,这条赋值语句会怎么样?
  2. 如果foo不是直接存在于myObject中,这条赋值语句会怎么样?
  3. 如果foo既出现在myObject中也出现在myObject的[[Prototype]]链上层,会发生什么?

屏蔽的情况更加复杂:

  1. 如果foo在[[Prototype]]链上层,并且没有被标记为只读,会发生什么?
  2. 如果foo在[[Prototype]]链上层,并有被标记为只读,会发生什么?
  3. 如果foo在[[Prototype]]链上层,并且它是一个setter,会发生什么?

如果你希望第二种和第三种情况下也屏蔽foo,那就不能使用=,而是使用什么呢?

var anotherObject = {
  a: 2
}

var myObject = Object.create(anotherObject)

anotherObject.a;
myObject.a;

anotherObject.hasOwnProperty("a")
myObject.hasOwnProperty("a")

myObject.a++;

anotherObject.a;
myObject.a;

myObject.hasOwnProperty("a")

上面的🌰输出什么?

5.2 "类"

5.2.1 "类"函数

function Foo(){
  // ...
}
var a = new Foo()

Object.getPrototypeOf(a) === Foo.prototype;

上面的🌰输出什么?

继承意味着复制操作,JS(默认)并不会复制对象属性。相反,JS会在两个对象之间创建一个关联,这样一个对象就可以通过委托访问另一个对象的属性和函数。

5.2.2 "构造函数"

function Foo(){
  // ...
}
Foo.prototype.constructor === Foo;

var a = new Foo();
a.constructor === Foo;

new会劫持所有普通函数并用构造对象的形式来调用它。

function NothingSpecial(){
  console.log("Don't mind me! ")
}

var a = new NothingSpecial();

a;

5.2.3 技术

function Foo(name){
  this.name = name;
}

Foo.prototype.myName = function(){
  return this.name;
}

var a = new Foo("a")
var b = new Foo("b")

a.myName()
b.myName()

上面的🌰输出什么?

function Foo(){/*...*/}

Foo.prototype = {/*...*/}

var a1 = new Foo();
a1.constructor === Foo;
a1.constructor === Object;

上面的🌰输出什么?constructor能表示被由...构造吗?

5.3 (原型) 继承

function Foo(name){
  this.name = name
}

Foo.prototype.myName = function(){
  return this.name
}

function Bar(name, label){
  Foo.call(this, name)
  this.label = label
}
// 我们创建了一个新的Bar.prototype对象并关联到Foo.prototype
Bar.prototype = Object.create(Foo.prototype)

Bar.prototype.myLabel = function(){
  return this.label
}

var a = new Bar("a", "obj a")

a.myName();
a.myLabel();

上面的🌰输出什么?

Bar.prototype = Foo.prototype;

假如用上面这种方式关联对象有什么问题?

Bar.prototype = new Foo()

假如用上面这种方式关联对象又有什么问题?

检查"类"关系

function Foo(){
  // ...
}
Foo.prototype.blah = ...;

var a = new Foo();

有哪三种方式能找出a的"祖先"?

5.4 对象关联

5.4.1 创建关联

var foo = {
  something: function(){
    console.log("Tell me something good...")
  }
}
var bar = Object.create(foo);
bar.something();

上面的🌰输出什么?

如何实现Object.create(...)的功能?

var anotherObject = {
  a: 2
}

var myObject = Object.create(anotherObject, {
  b: {
    enumerable: false,
    writable: true,
    configurable: false,
    value: 3
  },
  c: {
    enumerable: true,
    writable: false,
    configurable: false,
    value: 4
  }
})

myObject.hasOwnProperty("a");
myObject.hasOwnProperty("b");
myObject.hasOwnProperty("c");

myObject.a;
myObject.b;
myObject.c;

上面的🌰输出什么?

5.4.2 关联关系是备用

var anotherObject = {
  cool: function(){
    console.log("cool! ")
  }
}

var myObject = Object.create(anotherObject);

myObject.coo();

上面的🌰输出什么?

第6章 行为委托

原型链是什么?

6.1 面向委托的设计

6.1.1 类理论

首先定义一个通用父(基)类,可以将其命名为Task,在Task类中定义所有任务都有的行为。接着定义子类XYZ和ABC,它们都继承自Task并会添加一些特殊的行为来处理对应的任务。

class Task {
  id;
  // 构造函数Task()
  
  Task(ID) { id = ID; }
  outoutTask() { output(id); }
}

class XYZ inherits Task {
  label;
  
  // 构造函数XYZ()
  XYZ(ID, Label) { 
    super(ID); 
    lable = Label;
  }
  outputTask() {
    super();
    output(label);
  }
}

class ABC inherits Task{
  // ...
}

6.1.2 委托理论

现在试着使用委托行为而不是类来思考同样的问题。

委托行为意味着某些对象(XYZ)在找不到属性或者方式引用时会把这个请求委托给另一个对象(Task)。

委托行为有什么区别呢?

function Foo(){}

var a1 = new Foo();

a1;

上面🌰在Chrome开发者工具的控制台中结果是什么?上面🌰在Firefox中结果又是什么?

function Foo(){}

var a1 = new Foo();

a1.constructor;
a1.constructor.name;

上面的🌰在Chrome中输出什么?

function Foo(){}

var a1 = new Foo();

Foo.prototype.constructor = function Gotcha(){}

a1.constructor;
a1.constructor.name;
a1;

上面的🌰在Chrome中输出什么?

var Foo = {};

var a1 = Object.create(Foo);

a1;

Object.defineProperty(Foo, "constructor", {
  enumerable: false,
  value: function Gotcha(){}
})

a1;

上面的🌰在Chrome中输出什么?

6.1.3 比较思维模型

下面是典型的("原型")面向对象风格:

function Foo(who){
  this.me = who;
}
Foo.prototype.identify = function(){
  return "I am " + this.me;
}
function Bar(who){
  Foo.call(this, who)
}
Bar.prototype = Object.create(Foo.prototype);

Bar.prototype.speak = function(){
  alert("Hello, "+this.identify()+".")
}
var b1 = new Bar("b1")
var b2 = new Bar("b2")

b1.speak();
b2.speak();

下面使用对象关联的风格来编写功能完全相同的代码:

Foo = {
  init: function(who){
    this.me = who;
  },
  identify: function(){
    return "I am" + this.me;
  }
}
Bar = Object.create(Foo)

Bar.speak = function(){
  alert("Hello, " + this.identify()+".")
}

var b1 = Object.create(Bar);
b1.init("b1");
var b2 = Object.create(Bar);
b2.init("b2")

b1.speak();
b2.speak();

对比一下这两种方式。