几个关于类的前提概念:
- 类/继承描述了一种代码的组织结构形式,类意味着复制;
- 类的核心概念:类、继承、实例化和多态【多态可以简述为父类的通用行为被子类用更特殊的行为重写】;
- javascript不存在类,但基于类是一种设计模式这一前提,js通过一些方法近似实现了类的功能;
1.工厂模式
// 工厂模式
function person(name, age, title) {
var o = {}
o.name = name;
o.age = age;
o.title = title;
o.saySomething = function() {
console.log(this.name);
}
return o
}
var person1 = person('孙悟空', 18, '齐天大圣')
var person2 = person('猪八戒', 26, '天蓬元帅')
console.log(person1);
console.log(person2);
打印结果
{name: "孙悟空", age: 18, title: "齐天大圣", saySomething: ƒ}
age: 18
name: "孙悟空"
saySomething: ƒ ()
title: "齐天大圣"
__proto__:
constructor: ƒ Object()
hasOwnProperty: ƒ hasOwnProperty()
isPrototypeOf: ƒ isPrototypeOf()
propertyIsEnumerable: ƒ propertyIsEnumerable()
toLocaleString: ƒ toLocaleString()
toString: ƒ toString()
valueOf: ƒ valueOf()
__defineGetter__: ƒ __defineGetter__()
__defineSetter__: ƒ __defineSetter__()
__lookupGetter__: ƒ __lookupGetter__()
__lookupSetter__: ƒ __lookupSetter__()
get __proto__: ƒ __proto__()
set __proto__: ƒ __proto__()
问题
- 创建的对象之间没有任何联系,缺乏可识别的类似类的特性;
2.构造函数
// 构造函数模式
function Person(name, age, title) {
this.name = name;
this.age = age;
this.title = title;
this.saySomething = function() {
console.log(this.name);
}
}
var person1 =new Person('孙悟空', 18, '齐天大圣')
var person2 =new Person('猪八戒', 26, '天蓬元帅')
console.log(person1);
console.log(person2);
打印结果
Person {name: "孙悟空", age: 18, title: "齐天大圣", saySomething: ƒ}
age: 18
name: "孙悟空"
saySomething: ƒ ()
title: "齐天大圣"
__proto__:
constructor: ƒ Person(name, age, title)
__proto__:
constructor: ƒ Object()
hasOwnProperty: ƒ hasOwnProperty()
isPrototypeOf: ƒ isPrototypeOf()
propertyIsEnumerable: ƒ propertyIsEnumerable()
toLocaleString: ƒ toLocaleString()
toString: ƒ toString()
valueOf: ƒ valueOf()
__defineGetter__: ƒ __defineGetter__()
__defineSetter__: ƒ __defineSetter__()
__lookupGetter__: ƒ __lookupGetter__()
__lookupSetter__: ƒ __lookupSetter__()
get __proto__: ƒ __proto__()
set __proto__: ƒ __proto__()
特征
- 没有显式的创建对象
- 属性和方法直接赋值给this对象
- 没有return语句
- __ proto __属性中保存着constructor【构造函数】属性,该属性指向Person
问题:
- 方法的重新创建会造成内存的浪费
- 将方法定义在全局,然后将引用赋值给this.saySomething,可以解决重复创建多个相同函数的问题,但又会引入方法缺乏封装性的问题
3.原型模式
函数的prototype属性:

打印构造函数Person的结果
向构造函数的原型上添加属性和方法:
function Person() {
}
// 向构造函数的原型上添加属性和方法
Person.prototype.name = '孙悟空';
Person.prototype.age = 18;
Person.prototype.title = '齐天大圣';
Person.prototype.saySomething = function() {
console.log(this.name);
}
var person1 =new Person()
var person2 =new Person()
console.log(person1);
console.log(person2);
console.dir(Person);
实例对象和构造函数的打印结果

__ proto __和prototype的解析:

Snipaste_2020-09-12_15-53-29.png
4.原型模式语法的精简
function Person() {}
// 向构造函数的原型上添加属性和方法
Person.prototype = {
name: '孙悟空',
age: 18,
title: '齐天大圣',
saySomething: function () {
console.log(this.name);
}
}
var person1 = new Person()
var person2 = new Person()
console.log(person1);
console.log(person2);
console.dir(Person);
打印结果

这里对prototype赋值对象,完全重写了prototype,通过以下代码保证constructor的指向;
Person.prototype = {
// 保证contructor的指向
constructor: Person,
name: '孙悟空',
age: 18,
title: '齐天大圣',
saySomething: function () {
console.log(this.name);
}
}
注意: 此时的constructor的可遍历性为真,原生的是不可遍历的,可通过Object.defineProperty()进行数据属性的设置;
感觉挺重要的3点:
- 实例与原型之间的连接只不过是一个指针,而非是一个副本;
- 原型具有动态性,即可以先创建实例,之后再在原型上添加新属性或方法,实例依然能够读取原型上的属性或方法;
- 重写原型会丧失这种动态性
原型对象的问题:
function Person() {}
// 向构造函数的原型上添加属性和方法
Person.prototype = {
// 保证contructor的指向
constructor: Person,
name: '孙悟空',
age: 18,
title: '齐天大圣',
friends: ['八戒', '沙僧'],
saySomething: function () {
console.log(this.name);
}
}
var person1 = new Person()
var person2 = new Person()
//修改其中一个实例person1中的引用类型的属性值
person1.friends.push('哪吒')
console.log(person1);
console.log(person2);
console.dir(Person);
打印结果

注:基本属性的值会产生屏蔽,即在实例上修改同名属性,会在实例上创建属性,并且通过实例访问时,会遮蔽原型上的同名属性;
5.构造函数和原型的结合
简述就是用构造函数来定义实例属性【保证实例属性的独立性】,原型模式用于定义方法和共享属性
function Person(name, age, title) {
this.name = name;
this.age = age;
this.title = title;
// 这样实例的引用类型的属性就是独立的了
this.friends = ['八戒', '沙僧']
this.saySomething = function () {
console.log(this.name);
}
}
Person.prototype = {
constructor: Person,
saySomething: function () {
console.log(this.name);
}
}
var person1 = new Person('孙悟空', 18, '齐天大圣')
var person2 = new Person('猪八戒', 26, '天蓬元帅')
console.log(person1);
console.log(person2);
console.dir(Person);