js创建对象方案
创建对象方案-工厂模式
// 工厂模式: 工厂函数
function createPerson(name, age, height) {
var p = {}
p.name = name
p.age = age
p.height = height
return p
}
var p1 = createPerson('Fhup1', 19, 1.88)
var p2 = createPerson('Fhup2', 18, 1.78)
var p3 = createPerson('Fhup3', 17, 1.68)
// 工厂模式的缺点: 获取不到对象真实的类型
console.log(p1)
console.log(p2)
console.log(p3)
认识构造函数
/**
* 构造函数: 称之为构造器(constructor),创建对象时才会调用的函数
* 构造函数也是一个普通的函数,和千千万万普通的函数没有区别
*/
function foo() {
console.log('foo~~~');
}
// 直接调用foo就是一个普通的函数
// foo()
// new调用foo就是一个构造函数
new foo // new调用 可以不写括号,括号作用是传参数
const fn = new foo
console.log(fn);
/**
* new 调用的过程
* 1.创建一个新的对象 { }
* 2.新对象内部的[[prototype]]属性被赋值为该构造函数的prototype属性:
* fn.__proto__ = foo.prototype
* 3.内部的this指向创建出来的对象
* 4.执行剩余的函数体代码
* 5.返回创建出来的新对象
*/
创建对象方案-构造函数
// 规范: 构造函数的首字母大写
function Person(name, age, height) {
this.name = name // 内部this就是创建出来的p1
this.age = age
this.height = height
this.eat = function(){
console.log(this.name+' eating');
}
}
const p1 = new Person('Fhup', 18, 1.88)
console.log(p1); // 类型为 Person
p1.eat()
对象的原型理解
// 每个对象都有特殊的内置属性[[prototype]],这个属性称之为对象的原型: 隐式原型(__proto__).
var obj = { name: 'Fhup' }
// 为了让我们能看到这个属性,浏览器给对象(函数也是对象)中提供了一个__proto__属性(在ECMA不存在)
console.log(obj.__proto__) // {}
// 内部实现
// var obj = { name: 'Fhup', __proto__: {} }
// 获取原型(ES5之后)
console.log(Object.getPrototypeOf(obj))
obj.__proto__.age = 20
console.log(obj.age);
// 原型的作用
// 当我们从一个对象中获取某一个属性时,它会触发 [[get]] 操作
// 1.获取时先在当前对象找,找到直接使用
// 2.没有找到,沿着其原型链(__proto__)查找
函数的原型理解
function foo() {
// 进行模拟,内部自动实现
// var moni = {}
// this = moni
// this.__proto__ = foo.prototype
// return moni
}
// 函数也是一个对象
console.log(foo.__proto__) //函数作为对象,也是有[[prototype]] 隐式原型
// 因为是函数,它会多出来一个属性: prototype 显式原型(只有函数有这个属性)
console.log(foo.prototype); // {}
var f1 = new foo()
var f2 = new foo()
// new调用的过程 第二步:创建并返回的新对象内部的[[prototype]]属性被赋值为该构造函数的prototype属性
console.log(f1.__proto__ === foo.prototype) // true
console.log(f2.__proto__ === foo.prototype)
构造函数原型内存图
function Person() {
}
var p1 = new Person()
var p2 = new Person()
p2.__proto__.name = 'Fhup1' // p1和p2指向同一个原型对象prototype
// p2.__proto__ 指向 函数的prototype(原型对象), name属性添加在函数的prototype里面.
// p1.__proto__ 等价于 Person.prototype
console.log(p1.name); // Fhup1
// 去p1里面去找name值,找不到时,去 p1.__proto__找,再去函数的prototype对象里面找.
函数原型上的属性
function foo() {
}
// 1.constructor属性
console.log(new foo().__proto__) // {}
console.log(foo.prototype) // {}
// foo.prototype这个对象里面有一个constructor属性
// console.log(Object.getOwnPropertyDescriptors(foo.prototype));
// prototype.constructor指向构造函数本身
// console.log(foo.prototype.constructor) // [Function: foo]
// console.log(foo.prototype.constructor.name) // 函数名
// console.log(foo.prototype.constructor.prototype.constructor); // 相互调用
// 2.添加自己的属性
// foo.prototype.name = 'Fhup'
// var f1 = new foo()
// var f2 = new foo()
// f1.__proto__.age = 12 // f1.__proto__ -> foo.prototype, 还是给foo.prototype添加age值
// console.log(f1.name);
// console.log(f2.age);
// 3.直接修改整个prototype对象
// 注意: 原型用字面量定义时,会与实例断开链接
foo.prototype = {
// constructor: foo, // constructor指向foo,通过Object.defineProperty()设置
name: 'Fhup',
age: 18,
height: 1.88
}
var f = new foo() // 原型用字面量定义时,会与实例断开链接,将实例放在下方即可
console.log(f.name, f.age, f.height);
/**
* 上面添加的constructor,是可以进行枚举(for-in,Object.keys())
* 默认的constructor是不能进行枚举的
* 真实开发中,通过Object.defineProperty方式添加constructor
*/
Object.defineProperty(foo.prototype,'constructor',{ // 对函数原型的constructor属性进行操作
value: foo,
enumerable: false,
configurable: true,
writable: true
})
创建对象方案-原型和构造函数
function Person(name, age, address) {
this.name = name
this.age = age
this.address = address
}
Person.prototype.say = function() {
console.log(this.name + ' is saying'); // 谁调用this就为谁
}
var p1 = new Person('Fhup', 18, 1.88)
var p2 = new Person('kobe', 19, 1.98)
console.log(p1.name);
p1.say()
p2.say()