js创建对象方案

64 阅读3分钟

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对象里面找.

image.png

函数原型上的属性

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()