在JS世界中,万物皆对象
可以把世界上任何事物抽象为对象,对象中有描述事物的属性以及该事物有哪些功能的方法
比如: 小狗
它在程序的世界中就被抽象为有name、age、likeFood属性 和 sayHi方法的代码块
依照这种模式,任何事物都可以提取它们的属性和方法构造成JS中的对象
// 在一些面向对象的语言中, 我们用类(class)的概念去描述一个对象
// class Dog
{
name, 狗狗的姓名
age, 狗狗的年龄
likeFood, 狗狗喜欢的食物
sayHi 狗狗会打招呼
}
// 而在JavaScript中使用了一个完全不同的术语,类并不完全是一个对象,更像是一个定义对象特质的一个模板
我们平常使的最常用创建对象的方式是对象字面量形式:
// 对象字面量
let obj = {
name: 'ckn',
age: 18
}
// new 操作符
let object = new Object({
name: 'lxh',
age: 16,
sayHi: function(){
console.log('hi my name is ' + this.name)
}
})
object = new Object();
object.name = 'zlp';
object.age = 18;
object.sayHi = function() {
console.log('hi my name is ' + this.name
}
向上述对象字面量和new操作符生成对象,如果只是简单的生成几个对象,还是很方便的,但是当我们需要越来越多的同类型的对象,比如创建一万只小狗对象,则上面两种方法需要书写一万遍,代码冗余度极高,影响运行性能,代码也不美观,所以出现了工厂函数的概念
function createDog(name, age){
var obj = new Object();
obj.name = name;
obj.age = age;
obj.sayHi = function() {
console.log('hi my name is ' + this.name)
}
// important 需要把对象当作函数返回值返回
return obj;
}
let bob = createDog('bob',2);
let shary = createDog('shary',3);
// ... 可以创建更多狗狗
工厂函数的出现很大程度上解决了代码冗余度的问题,但是又出现了另外一个问题,假如我们创造一万只狗狗 和 一万只猫猫用工厂函数的形式,会出现你分不清tom和bob哪个是猫猫和哪个是狗狗,因为你控制台console.log打印的话它们都是Object对象类型。(当然你自己创建的你自己是知道的,如果是别人创建的呢,你还能确定tom代表的就一定是猫猫吗?)
function createCat(name, age){
var obj = new Object();
obj.name = name;
obj.age = age;
obj.sayHi = function() {
console.log('hi my name is ' + this.name)
}
// important 需要把对象当作函数返回值返回
return obj;
}
let tom = createCat('cat',2);
为了解决上述辨别对象类型的问题,可以使用构造函数模式来解决
MDN: 不像“经典”的面向对象的语言,从构造函数创建的新实例的特征并非全盘复制,而是通过一个叫做原形链的参考链链接过去的。所以这并非真正的实例,严格的讲, JavaScript 在对象间使用和其它语言的共享机制不同。
function Cat(name, age) {
this.name = name;
this.age = age;
this.sayHi = function() {
console.log('hi my name is ' + this.name)
}
}
function Dog(name, age) {
this.name = name;
this.age = age;
this.sayHi = function() {
console.log('hi my name is ' + this.name)
}
}
let tom = new Cat('tom', 2);
let bob = new Dog('bob', 2);
console.log(tom); // Cat {name: "tom", age: 2, sayHi: ƒ}
console.log(bob); // Dog {name: "bob", age: 2, sayHi: ƒ}
// tom是Cat构造函数的实例对象 bob是Dog构造函数的实例对象
// instance of 可以查看对象是否某构造函数的实例对象
// 现在我们就可以确切的分别出tom是猫猫,bob是狗狗了
其实构造函数已经完美解决普通方式创建新对象所带来代码冗余问题,但是还是有个小问题,那就是关于sayHi方法的,sayHi方法是个函数对象(函数对象其实是构造函数Function的实例对象),属于引用数据类型,会在堆内存中占用空间,我们每次new出一个新的实例对象,都会重新创建出一个函数(实例)对象,但是这个函数对象内部代码做的又都是同一样的事情(打招呼),至此,针对sayHi方法我们还是有可优化的空间,那就是使用原型对象模式
function Cat(name, age) {
this.name = name;
this.age = age;
}
Cat.prototype.sayHi = function() {
console.log('hi my name is ' + this.name)
}
let gary = new Cat('gary', 1);
gary.sayHi(); // hi my name is gary
let lucy = new Cat('lucy', 4);
lucy.sayHi(); // hi my name is lucy
...
// 以上生成的每个实例对象都有sayHi方法,且sayHi方法只定义了一次,实现了共用。
Object.create()方法创建一个新的对象:基于现有对象创建新的对象
MDN: Object.create()方法创建一个新对象,使用现有的对象来提供新创建对象的_ proto_
let carry = Object.create(bob);
console.log(carry); // Dog {}
// 它是一个空的Dog类对象,在其原型对象上有name,age,和sayHi方法
// carry.name => bob carry.age => 2
通过构造函数生成实例对象,通过new操作符生成对象,其构造函数内部是怎么实现的?
- 首先创建一个新对象
- 将构造函数的上下文对象this赋值给新对象(this指向新创建的对象)
- 顺序执行构造函数内部代码
- 将新创建的对象当做返回值返回