文章目录
在对象上创建方法
let duck = {
name: "Aflac",
numLegs: 2,
sayName: function() {return "The name of this duck is " + duck.name + ".";}
};
duck.sayName();
使用 this 关键字提高代码重用性
sayName: function() {return "The name of this duck is " + duck.name + ".";}
let duck = {
name: "Aflac",
numLegs: 2,
sayName: function() {return "The name of this duck is " + this.name + ".";}
};
this 指向的就是与这个方法有关联的 duck 对象。避免变量名发生改变而造成错误
定义构造函数
Constructors 是创建对象的函数。 函数给这个新对象定义属性和行为。 可将它们视为创建的新对象的蓝图。
function Bird() {
this.name = "Albert";
this.color = "blue";
this.numLegs = 2;
}
构造函数函数名的首字母大写,这是为了方便我们区分构造函数( constructors)和其他非构造函数。
构造函数使用 this 关键字来给它将创建的这个对象设置新的属性。 在构造函数里面,this 指向的就是它新创建的这个对象。
构造函数定义了属性和行为就可创建对象,而不是像其他函数一样需要设置返回值。
let blueBird = new Bird();
blueBird 这个实例继承了Bird 构造函数的所有属性
> 接收参数
function Bird(name, color) {
this.name = name;
this.color = color;
this.numLegs = 2;
}
let cardinal = new Bird("Bruce", "red");
使用 instanceof 验证对象的构造函数
instanceof 将对象与构造函数之间进行比较,根据对象是否由这个构造函数创建的返回 true 或者 false
let Bird = function(name, color) {
this.name = name;
this.color = color;
this.numLegs = 2;
}
let crow = new Bird("Alexis", "black");
instanceof 方法会返回 true
crow instanceof Bird;
一次修改所有对象的实例 prototype
prototype 是一个可以在所有 Bird 实例之间共享的对象
Bird.prototype.numLegs = 2;
所有的 Bird 实例都新增了共同的 numLegs 属性值
可以把 prototype 看作是创建对象的 "配方"
自身属性和原型属性
自身属性是直接在对象上定义的。 而原型属性在 prototype 上定义
function Bird(name) {
this.name = name; //own property
}
Bird.prototype.numLegs = 2; // prototype property
let duck = new Bird("Donald");
构造函数属性 constructor 查询实例对应的是哪个对象
let duck = new Bird();
let beagle = new Dog();
console.log(duck.constructor === Bird);
console.log(beagle.constructor === Dog);
两次都显示 true
constructor 属性是对创建这个实例的构造函数的一个引用。 constructor 属性的一个好处是可以通过检查这个属性来找出它是一个什么对象。
将原型更改为新对象 (添加多个属性)
Bird.prototype = {
constructor: Bird, #手动设置一个新对象的原型会清除 constructor 属性.所以要在原型对象中定义一个 constructor 属性
numLegs: 2,
eat: function() {
console.log("nom nom nom");
},
describe: function() {
console.log("My name is " + this.name);
}
};
给对象的 prototype 设置为一个已经包含了属性的新对象.所有属性都可以一次性添加进来
对象的原型来自哪里
Bird.prototype.isPrototypeOf(duck);
返回true
avaScript 中所有的对象(除了少数例外)都有自己的 prototype。 而且,对象的 prototype 本身也是一个对象
因为 prototype 是一个对象,所以 prototype 对象也有它自己的 prototype! Bird.prototype 的 prototype 就是 Object.prototype
Object.prototype.isPrototypeOf(Bird.prototype);
let duck = new Bird("Donald");
duck.hasOwnProperty("name");
hasOwnProperty 是定义在 Object.prototype 上的一个方法,尽管在 Bird.prototype 和 duck上并没有定义该方法,但是我们依然可以在这两个对象上访问到。这就是 prototype 链的一个例子。
在这个prototype 链中,Bird 是 duck 的 supertype,而 duck 是 subtype。 Object 则是 Bird 和 duck 实例共同的 supertype。 Object 是 JavaScript 中所有对象的 supertype,也就是原型链的最顶层。 因此,所有对象都可以访问 hasOwnProperty 方法。
使用继承避免重复
编写重复代码会产生的问题是:任何改变都需要去多个地方修复所有重复的代码。 这通常意味着我们需要做更多的工作,会产生更高的出错率。
Bird.prototype = {
constructor: Bird,
describe: function() {
console.log("My name is " + this.name);
}
};
Dog.prototype = {
constructor: Dog,
describe: function() {
console.log("My name is " + this.name);
}
};
describe 方法在两个地方重复定义了
可以通过创建一个 Animal supertype(或者父类)来重写这段代码
function Animal() { };
Animal.prototype = {
constructor: Animal,
describe: function() {
console.log("My name is " + this.name);
}
};
从超类继承行为
Animal 超类(supertype
function Animal() { }
Animal.prototype.eat = function() {
console.log("nom nom nom");
};
创建 Animal 实例
let animal = new Animal();
此语法用于继承时会存在一些缺点,可以用另外一种没有这些缺点的方法来替代 new 操作
let animal = Object.create(Animal.prototype);
Object.create(obj) 创建了一个新对象,并指定了 obj 作为新对象的 prototype。
animal.eat();
animal instanceof Animal;
将子辈的原型设置为父辈的实例
ChildObject.prototype = Object.create(ParentObject.prototype);
给子类型(或者子类)设置 prototype
Bird.prototype = Object.create(Animal.prototype);
prototype 类似于创建对象的“配方”。 从某种意义上来说,Bird 对象的配方包含了 Animal 的所有关键“成分
let duck = new Bird("Donald");
duck.eat();
duck 继承了Animal 的所有属性,其中包括了 eat 方法
重置一个继承的构造函数属性
当一个对象从另一个对象那里继承了其 prototype 时,那它也继承了父类的 constructor 属性
function Bird() { }
Bird.prototype = Object.create(Animal.prototype);
let duck = new Bird();
duck.constructor
#duck 和其他所有 Bird 的实例都应该表明它们是由 Bird 创建的,而不是由 Animal 创建的
#手动将 Bird 的构造函数属性设置为 Bird 对象
Bird.prototype.constructor = Bird;
继承后添加方法
function Animal() { }
Animal.prototype.eat = function() {
console.log("nom nom nom");
};
function Bird() { }
Bird.prototype = Object.create(Animal.prototype);
Bird.prototype.constructor = Bird;
Bird.prototype.fly = function() {
console.log("I'm flying!");
};
现在 Bird 的实例中就有了 eat() 和 fly() 这两个方法
重写继承的方法
ChildObject.prototype = Object.create(ParentObject.prototype);
ChildObject.prototype.methodName = function() {...};
Bird 重写了从 Animal 继承来的 eat() 方法
function Animal() { }
Animal.prototype.eat = function() {
return "nom nom nom";
};
function Bird() { }
Bird.prototype = Object.create(Animal.prototype);
Bird.prototype.eat = function() {
return "peck peck peck";
};
如果你有一个实例:let duck = new Bird();,然后你调用了 duck.eat(),以下就是 JavaScript 在 duck 的 prototype 链上寻找方法的过程:
duck => eat() 是定义在这里吗? 不是。
Bird => eat() 是定义在这里吗? => 是的。 执行它并停止往上搜索。
Animal => 这里也定义了 eat() 方法,但是 JavaScript 在到达这层原型链之前已停止了搜索。
Object => JavaScript 在到达这层原型链之前也已经停止了搜索。
使用 Mixin 在不相关对象之间添加共同行为
mixin 允许其他对象使用函数集合
let flyMixin = function(obj) {
obj.fly = function() {
console.log("Flying, wooosh!");
}
};
let bird = {
name: "Donald",
numLegs: 2
};
let plane = {
model: "777",
numPassengers: 524
};
flyMixin(bird);
flyMixin(plane);
这里的 flyMixin 接收了bird 和 plane 对象,然后将 fly 方法分配给了每一个对象。 现在 bird 和 plane 都可以飞行了:
bird.fly();
plane.fly();
使用闭包保护对象内的属性不被外部修改
使属性私有化最简单的方法就是在构造函数中创建变量。 可以将该变量范围限定在构造函数中,而不是全局可用。 这样,属性只能由构造函数中的方法访问和更改。
function Bird() {
let hatchedEgg = 10;
this.getHatchedEggCount = function() {
return hatchedEgg;
};
}
let ducky = new Bird();
ducky.getHatchedEggCount();
了解立即调用函数表达(IIFE)
这是一个匿名函数表达式,立即执行并输出 Chirp, chirp!
(function () {
console.log("Chirp, chirp!");
})();
请注意,函数没有名称,也不存储在变量中。 函数表达式末尾的两个括号()会让它被立即执行或调用。 这种模式被叫做立即调用函数表达式(immediately invoked function expression) 或者IIFE。
使用 IIFE 创建一个模块
一个立即调用函数表达式(IIFE)通常用于将相关功能分组到单个对象或者是 module 中。
function glideMixin(obj) {
obj.glide = function() {
console.log("Gliding on the water");
};
}
function flyMixin(obj) {
obj.fly = function() {
console.log("Flying, wooosh!");
};
}
将这些 mixins 分成以下模块
let motionModule = (function () {
return {
glideMixin: function(obj) {
obj.glide = function() {
console.log("Gliding on the water");
};
},
flyMixin: function(obj) {
obj.fly = function() {
console.log("Flying, wooosh!");
};
}
}
})();
注意:一个立即调用函数表达式(IIFE)返回了一个 motionModule 对象。 返回的这个对象包含了作为对象属性的所有 mixin 行为。 module 模式的优点是,所有的运动相关的行为都可以打包成一个对象,然后由代码的其他部分使用。
motionModule.glideMixin(duck);
duck.glide();