工厂模式
创建多个对象
var p1 = {
name: "张三",
age: 18,
height: 1.88,
address: "广州市",
eating: function() {
console.log(this.name + "在吃东西~")
},
running: function() {
console.log(this.name + "在跑步~")
}
}
var p2 = {
name: "李四",
age: 20,
height: 1.98,
address: "北京市",
eating: function() {
console.log(this.name + "在吃东西~")
},
running: function() {
console.log(this.name + "在跑步~")
}
}
var p3 = {
name: "王五",
age: 30,
height: 1.78,
address: "上海市",
eating: function() {
console.log(this.name + "在吃东西~")
},
running: function() {
console.log(this.name + "在跑步~")
}
}
创建同样的对象时,需要编写重复的代码;
工厂模式其实是一种常见的设计模式;
通常我们会有一个工厂方法,通过该工厂方法我们可以产生想要的对象
// 工厂模式: 工厂函数
function createPerson(name, age, height, address) {
var p = {}
p.name = name
p.age = age
p.height = height;
p.address = address
p.eating = function() {
console.log(this.name + "在吃东西~")
}
p.running = function() {
console.log(this.name + "在跑步~")
}
return p
}
var p1 = createPerson("张三", 18, 1.88, "广州市")
var p2 = createPerson("李四", 20, 1.98, "上海市")
var p3 = createPerson("王五", 30, 1.78, "北京市")
// 工厂模式的缺点(获取不到对象最真实的类型)
console.log(p1, p2, p3)
构造函数
工厂方法创建对象有一个比较大的问题:我们在打印对象时,对象的类型都是Object类型但是从某些角度来说,这些对象应该有一个他们共同的类型;
下面我们来看一下另外一种模式:构造函数
构造函数也称之为构造器(constructor),通常是我们在创建对象时会调用的函数;
在其他面向的编程语言里面,构造函数是存在于类中的一个方法,称之为构造方法;
但是JavaScript中的构造函数有点不太一样; JavaScript中的构造函数是怎么样的?
构造函数也是一个普通的函数,从表现形式来说,和千千万万个普通的函数没有任何区别;
那么如果这么一个普通的函数被使用new操作符来调用了,那么这个函数就称之为是一个构造函数;
// 规范: 构造函数的首字母一般是大写
function Person(name, age, height, address) {
this.name = name
this.age = age
this.height = height
this.address = address
this.eating = function() {
console.log(this.name + "在吃东西~")
}
this.running = function() {
console.log(this.name + "在跑步")
}
}
var p1 = new Person("张三", 18, 1.88, "广州市")
var p2 = new Person("李四", 20, 1.98, "北京市")
console.log(p1)
console.log(p2)
p1.eating()
p2.eating()
//构造函数也是有缺点的,它在于我们需要为每个对象的函数去创建一个函数对象实例;
当我们通过new去调用一个函数时, 和通过的调用到底有什么区别? 如果一个函数被使用new操作符调用了,那么它会执行如下操作:
- 在内存中创建一个新的对象(空对象)
- 这个对象内部的[[prototype]]属性会被赋值为该构造函数的prototype属性;
- 构造函数内部的this,会指向创建出来的新对象;
- 执行函数的内部代码(函数体代码);
- 如果构造函数没有返回非空对象,则返回创建出来的新对象;
原型
- JavaScript当中每个对象都有一个特殊的内置属性 [[prototype]],这个特殊的对象可以指向另外一个对象。
- 那么这个对象有什么用呢?
- 当我们通过引用对象的属性key来获取一个value时,它会触发 [[Get]]的操作;
- 这个操作会首先检查该属性是否有对应的属性,如果有的话就使用它;
- 如果对象中没有改属性,那么会访问对象[[prototype]]内置属性指向的对象上的属性;
- 那么如果通过字面量直接创建一个对象,这个对象也会有这样的属性吗?如果有,应该如何获取这个属性呢?
- 方式一:通过对象的 proto 属性可以获取到(但是这个是早期浏览器自己添加的,存在一定的兼容性问题)
- 方式二:通过 Object.getPrototypeOf 方法可以获取到
// 我们每个对象中都有一个 [[prototype]], 这个属性可以称之为对象的原型(隐式原型)
var obj = { name: "why" } // [[prototype]]
var info = {} // [[prototype]]
// 1.解释原型的概念和看一下原型
// 早期的ECMA是没有规范如何去查看 [[prototype]]
// 给对象中提供了一个属性, 可以让我们查看一下这个原型对象(浏览器提供)
// __proto__
// console.log(obj.__proto__) // {}
// console.log(info.__proto__) // {}
// var obj = {name: "why", __proto__: {} }
// // ES5之后提供的Object.getPrototypeOf
// console.log(Object.getPrototypeOf(obj))
// 2.原型有什么用呢?
// 当我们从一个对象中获取某一个属性时, 它会触发 [[get]] 操作
// 1. 在当前对象中去查找对应的属性, 如果找到就直接使用
// 2. 如果没有找到, 那么会沿着它的原型链去查找 [[prototype]]
// obj.age = 18
obj.__proto__.age = 18
console.log(obj.age)
function foo() {
}
// 函数也是一个对象
// console.log(foo.__proto__) // 函数作为对象来说, 它也是有[[prototype]] 隐式原型
// 函数它因为是一个函数, 所以它还会多出来一个显示原型属性: prototype
console.log(foo.prototype)
var f1 = new foo()
var f2 = new foo()
console.log(f1.__proto__ === foo.prototype)//true
console.log(f2.__proto__ === foo.prototype)//true
function foo() {
}
// 1.constructor属性
// foo.prototype这个对象中有一个constructor属性
// console.log(foo.prototype)
// console.log(Object.getOwnPropertyDescriptors(foo.prototype))
// Object.defineProperty(foo.prototype, "constructor", {
// enumerable: true,
// configurable: true,
// writable: true,
// value: "哈哈哈哈"
// })
// console.log(foo.prototype)
// prototype.constructor = 构造函数本身
// console.log(foo.prototype.constructor) // [Function: foo]
// console.log(foo.prototype.constructor.name)
// console.log(foo.prototype.constructor.prototype.constructor.prototype.constructor)
// 2.我们也可以添加自己的属性
// foo.prototype.name = "why"
// foo.prototype.age = 18
// foo.prototype.height = 18
// foo.prototype.eating = function() {
// }
var f1 = new foo()
console.log(f1.name, f1.age)
// 3.直接修改整个prototype对象
foo.prototype = {
// constructor: foo,
name: "why",
age: 18,
height: 1.88
}
var f1 = new foo()
console.log(f1.name, f1.age, f1.height)
// 真实开发中我们可以通过Object.defineProperty方式添加constructor
Object.defineProperty(foo.prototype, "constructor", {
enumerable: false,
configurable: true,
writable: true,
value: foo
})
function Person(name, age, height, address) {
this.name = name
this.age = age
this.height = height
this.address = address
}
Person.prototype.eating = function() {
console.log(this.name + "在吃东西~")
}
Person.prototype.running = function() {
console.log(this.name + "在跑步~")
}
var p1 = new Person("why", 18, 1.88, "北京市")
var p2 = new Person("kobe", 20, 1.98, "洛杉矶市")
p1.eating()
p2.eating()