const obj = {
name: 'dz',
age: 23
}
const obj = new Object()
obj.name = name
obj.age = age
对象没有一个具体的类型
, 都是Object类型//返回一个对象的函数===>工厂函数
function createPerson(name, age) {
var obj = {
name: name,
age: age,
setName: function (name) {
this.name = name
}
}
return obj
}
// 创建2个人
var p1 = createPerson('Tom', 12)
var p2 = createPerson('Bob', 13)
// p1/p2是Object类型
function createStudent(name, price) {
var obj = {
name: name,
price: price
}
return obj
}
var s = createStudent('张三', 12000)
// s也是Object
缺点:
类型确定
的对象,与上方工厂模式有所对比//定义类型
function Person(name, age) {
this.name = name
this.age = age
this.setName = function (name) {
this.name = name
}
}
var p1 = new Person('Tom', 12)
p1.setName('Jack')
console.log(p1.name, p1.age)
console.log(p1 instanceof Person)
function Student (name, price) {
this.name = name
this.price = price
}
var s = new Student('Bob', 13000)
console.log(s instanceof Student)
var p2 = new Person('JACK', 23)
console.log(p1, p2)
优点:
构造函数
创造的, 使用instanceof
来判断是否属于某个构造函数的实例缺点:
JavaScript中的构造函数是怎么样的?
function Person() {}
Person.prototype.name = 'lisi';
Person.prototype.say = function() {
alert(this.name);
}
Person.prototype.friends = ['xiaofeixa'];
var person1 = new Person();
优点:
say方法是共享的了,所有的实例的say方法都指向同一个。
可以动态的添加原型对象的方法和属性,并直接反映在对象实例上。
var person1 = new Person()
Person.prototype.showFriends = function() {
console.log(this.friends)
}
person1.showFriends() //['xiaofeixia']
缺点:
出现引用的情况下会出现问题具体见下面代码:
var person1 = new Person();
var person2 = new Person();
person1.friends.push('zhangsan');
console.log(person2.friends) //['xiaofeixia', 'zhangsan']
因为js对引用类型的赋值都是将地址存储在变量中,所以person1和person2的friends属性指向的是同一块存储区域。
第一次调用say方法或者name属性的时候会搜索两次,第一次是在实例上寻找say方法,没有找到就去原型对象(Person.prototype)上找say方法,找到后就会在实例上添加这些方法or属性。
所有的方法都是共享的,没有办法创建实例自己的属性和方法,也没有办法像构造函数那样传递参数。
但是有一个大问题就是, 大家都是共享的, 因此每一个实例都可能更改这个原型里面的属性, 后面创建的对象包含的属性会覆盖上次一创建的对象的属性
注意:
优点②中存在一个问题就是直接通过对象字面量给Person.prototype
进行赋值的时候会导致constructor
改变,所以需要手动设置,其次就是通过对象字面量给Person.prototype
进行赋值,会无法作用在之前创建的对象实例上
var person1 = new Person()
Person.prototype = {
name: 'Pan',
setName: function(name){
this.name = name
}
}
person1.setName() //Uncaught TypeError: person1.set is not a function(…)
这是因为对象实例和对象原型直接是通过一个指针链接的,这个指针是一个内部属性[[Prototype]],可以通过__proto__
访问。我们通过对象字面量修改了Person.prototype指向的地址,然而对象实例的__proto__
,并没有跟着一起更新,所以这就导致,实例还访问着原来的Person.prototype
,所以建议不要通过这种方式去改变Person.prototype
属性
对于constructor
丢失的解决办法:
手动添加
Person.prototype = {
constructor: Person,
name: 'Pan',
setName: function(name){
this.name = name
}
}
上面的方式虽然可以, 但是也会造成constructor的[[Enumerable]]特性被设置了true.
Object.defineProperty(Person.prototype, "constructor", {
enumerable: false;
value: Person
})
创建多个类型确定
的对象//在构造函数中只初始化一般函数
function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype.setName = function (name) {
this.name = name
}
var p1 = new Person('Tom', 23)
var p2 = new Person('Jack', 24)
console.log(p1, p2)
优点:
这种方式结合两者的有点, 每个实例拥有自己的属性和方法, 以及共享相同的方法, 用的较多一种模式
function Person(name, age) {
this.name = name
this.age = age
if(typeof this.sayname != 'function') {
Person.prototype.sayname = () => {
console.log(this.name)
}
}
}
const p1 = new Person('dz', 23)
console.log(p1.sayname) // -> dz
优点:
可以在初次调用构造函数的时候就完成原型对象的修改
修改能体现在所有的实例中
这里只在sayname
方法不存在的情况下才添加到原型中, 只会在初次调用
构造函数时才会执行.
这样的代码, 使得每个对象的name、age、sex都是各自的
(不共有), 然后函数写在原型上, 就又是共享的.
注意: 使用动态原型模式时, 不能
使用 对象字面量重写原型
. 如果在已经创建了实例的情况下重写原型, 那么就会切断现有实例与新原型之间的联系.
function Person(name) {
var o = new Object()
o.name = name
o.say = function() {
alert(this.name)
}
return o
}
var peron1 = new Person('xiaofeixia')
优点:
缺点:
instanceof
操作符来检测的话, 结果只能是 false
。function Person(name) {
var o = new Object()
o.say = function() {
alert(name)
}
}
var person1 = new Person('xiaofeixia');
person1.name // undefined
person1.say() //xiaofeixia
优点:
缺点:
instanceof
操作符来检测的话, 结果只能是 false
。