《javascript高级程序设计》学习笔记 | 6.2.建立对象

关注前端小讴,阅读更多原创技术文章

建立对象

  • 建立单个对象:Object 构造函数 和 对象字面量
  • 缺点:使用一个接口建立不少对象,产生大量重复代码

相关代码 →javascript

工厂模式

  • 抽象了建立具体对象的过程
  • 用函数来封装以特定接口建立对象的细节
function createPerson(name, age, job) {
  var o = new Object()
  o.name = name
  o.age = age
  o.job = job
  o.sayName = function () {
    console.log(this.name)
  }
  return o
}
var person1 = createPerson('Nicholas', 29, 'Engineer')
var person2 = createPerson('Greg', 27, 'Doctor')
console.log(person1)
console.log(person2)
  • 工厂模式解决了建立多个类似对象的问题,但没有解决对象识别问题——即怎样知道一个对象的类型

构造函数模式

  • 除了 Object 和 Array 等原生构造函数,还能够建立自定义的构造函数
function Person(name, age, job) {
  this.name = name
  this.age = age
  this.job = job
  this.sayName = function () {
    console.log(this.name)
  }
}
var person1 = Person('Nicholas', 29, 'Software Engineer')
var person2 = Person('Greg', 27, 'Doctor')
  • 构造函数模式 vs 工厂模式:① 不显式的建立对象;② 直接将属性和方法赋给 this 对象;③ 没有 return
  • 构造函数 new 一个对象后:① 建立了一个新对象;② 将构造函数的做用域(即 this)赋给新对象;③ 执行构造函数中的代码(即:为这个对象添加新属性);④ 返回新对象
  • 构造函数用大写字母开头,建立实例时用 new 操做符
  • 建立的对象的 constructor 属性指向构造函数
  • 建立的对象既是 Object 的实例,又是构造函数的实例
console.log(person1.constructor === Person) // true,constructor 属性指向构造函数
console.log(person2.constructor === Person) // true,constructor 属性指向构造函数
console.log(person1 instanceof Object) // true,是 Object 的实例
console.log(person1 instanceof Person) // true,也是 Person 的实例
console.log(person2 instanceof Object) // true,是 Object 的实例
console.log(person2 instanceof Person) // true,也是 Person 的实例
  • 能够将自定义构造函数的实例标识为一种特定的类型,这是构造函数模式赛过工厂模式的地方
  • 以该方法定义的构造函数是定义在 Global 对象中的,在浏览器中则是 window 对象
// 构造函数vs普通函数
var person3 = new Person('Nicholas', 29, 'Software Engineer') // 用构造函数建立对象
person3.sayName() // 'Nicholas'
Person('Greg', 27, 'Doctor') // 不使用new操做符,直接调用
global.sayName() // 直接调用函数时,this指向Global对象(浏览器中指向window对象)
var o = new Object() // 新对象o
var p = new Object() // 新对象p
Person.call(o, 'Kristen', 25, 'Nurse') // 扩充做用域,在对象o中调用Person()函数,call()分别传入每一个参数
Person.apply(p, ['Kristen', 25, 'Nurse']) // 扩充做用域,在对象p中调用Person()函数,apply()传入参数数组
o.sayName() // 'Kristen'
p.sayName() // 'Kristen'
  • 构造函数的问题在于,对象的每一个方法都要在每一个实例上从新建立一遍,既“每定义一个函数,就实例化一个对象”
  • 而建立 2 个完成一样任务的 Function 实例没有必要
function Person2(name, age, job) {
  this.name = name
  this.age = age
  this.job = job
  this.sayName = new Function(console.log(this.name)) // 与声明函数逻辑等价,每建立一个对象就要建立一个Function实例
}
console.log(person1.sayName === person2.sayName) // false,新对象的2个方法的做用域链和标识符解析不一样
  • 将对象的方法移到构造函数外部,避免屡次建立 Function 实例
function Person3(name, age, job) {
  this.name = name
  this.age = age
  this.job = job
  this.sayName = sayName
}
function sayName() {
  console.log(this.name) // 将sayName设置成全局函数
}
var person4 = new Person('Nicholas', 29, 'Software Engineer')
  • 构造函数仍未解决的问题:① 建立的全局函数实际上只须要被某个对象中调用;② 若对象有多个方法,则需建立不少全局对象

原型模式

  • 每一个函数都有 prototype 原型属性,该属性是一个指针,指向函数的原型对象,并包含特定类型的全部实例共享的属性和方法,即“经过调用构造函数-而建立的那个对象实例的-原型对象”
  • 使用原型对象的好处是,其全部对象实例共享其所包含的属性和方法

理解原型对象

function PersonPrototype() {}
PersonPrototype.prototype.name = 'Nicholas' // 为PersonPrototype的原型对象添加属性
PersonPrototype.prototype.age = 29 // 为PersonPrototype的原型对象添加属性
PersonPrototype.prototype.job = 'Software Engineer' // 为PersonPrototype的原型对象添加属性
PersonPrototype.prototype.sayName = function () {
  // 为PersonPrototype的原型对象添加方法
  console.log(this.name)
}
var person5 = new PersonPrototype()
var person6 = new PersonPrototype()
person5.sayName() // 'Nicholas'
person6.sayName() // 'Nicholas'
console.log(person5.sayName === person6.sayName) // true,prototype上建立的属性和方法,由新对象的全部实例共享
  • 原型对象自动得到 constructor(构造函数)属性,指向 prototype 属性所在函数的指针,即构造函数
console.log(PersonPrototype.prototype.constructor) // Function: PersonPrototype构造函数
console.log(PersonPrototype === PersonPrototype.prototype.constructor) // true,都指向构造函数
  • 实例内部包含[[Prototype]]指针,指向实例的构造函数的原型对象,但没有标准的方式访问[[Prototype]]
  • 在浏览器中,可用 __proto__ 属性实现[[Prototype]]的功能
console.log(person5.__proto__) // 原型对象,PersonPrototype {name: 'Nicholas',age: 29,job: 'Software Engineer',sayName: [Function] }
console.log(person5.__proto__ === PersonPrototype.prototype) // true,都指向原型对象
console.log(person5.__proto__.constructor) // Function: PersonPrototype构造函数
  • 原型对象的 isPrototypeOf()方法,检测实例中否有指向原型对象的指针
console.log(PersonPrototype.prototype.isPrototypeOf(person5)) // true,person5包含指向PersonPrototype的原型对象的指针
console.log(PersonPrototype.prototype.isPrototypeOf(person1)) // false,person1不包含指向PersonPrototype的原型对象的指针
  • ES5 追加 Object.getPrototypeOf()方法,参数为实例,返回实例的构造函数的原型对象
console.log(Object.getPrototypeOf(person5)) // 原型对象
console.log(Object.getPrototypeOf(person5) === person5.__proto__) // true,都指向原型对象
console.log(Object.getPrototypeOf(person5) === PersonPrototype.prototype) // true,都指向原型对象
console.log(Object.getPrototypeOf(person5).name) // 'Nicholas'
console.log(Object.getPrototypeOf(person5).constructor) // Function: PersonPrototype构造函数
  • 代码读取对象属性的搜索过程:前端

    • 1.搜索对象实例自己 -> 有属性 → 返回属性值 -> 结束
    • 2.对象实例自己无属性 -> 搜索原型对象 → 有/无属性 → 返回属性值/undefined → 结束
  • 能够经过实例访问原型中属性的值,但没法经过实例重写原型中属性的值
  • 若是添加的实例属性与原型的属性同名,则实例属性屏蔽原型中的属性
  • 删除同名的实例属性,可恢复被屏蔽的原型的属性
var person7 = new PersonPrototype()
person7.name = 'Greg'
console.log(person7.name) // 'Greg',来自实例
console.log(person5.name) // 'Nicholas',来自原型
delete person7.name
console.log(person7.name) // 'Nicholas',来自原型
  • 使用 hasOwnProperty('属性') 检测属性存在于实例 or 原型,存在于实例返回 true
var person8 = new PersonPrototype()
var person9 = new PersonPrototype()
console.log(person8.hasOwnProperty('name')) // false,name不存在在person8的实例中
person8.name = 'Simon'
console.log(person8.name) // 'Simon',来自实例
console.log(person8.hasOwnProperty('name')) // true,name存在在person8的实例中
console.log(person9.name) // 'Nicholas',来自原型
console.log(person9.hasOwnProperty('name')) // false,name不存在在person8的实例中
delete person8.name
console.log(person8.name) // 'Nicholas',来自原型
console.log(person8.hasOwnProperty('name')) // false,person8实例的name属性已被删除
  • 可在原型对象上调用 Object.getOwnPropertyDescriptor(),获取原型属性的描述符
console.log(Object.getOwnPropertyDescriptor(person8, 'name')) // undefined,person8实例上没有name属性
console.log(Object.getOwnPropertyDescriptor(person8.__proto__, 'name')) // {value: 'Nicholas',writable: true,enumerable: true,configurable: true},原型对象的name属性描述符

原型与 in 操做符

  • 单独使用 in:对象可以访问指定属性则返回 true,不管属性在实例中仍是原型中
function PersonIn() {}
PersonIn.prototype.name = 'Nicholas'
PersonIn.prototype.age = 29
PersonIn.prototype.job = 'Software Engineer'
PersonIn.prototype.sayName = function () {
  console.log(this.name)
}
var person9 = new PersonIn()
var person10 = new PersonIn()
console.log(person9.hasOwnProperty('name')) // false,实例person9中不含name属性
console.log('name' in person9) // true,经过person9能够访问到name属性
person9.name = 'Greg'
console.log(person9.name); // 'Greg',来自实例
console.log(person9.hasOwnProperty('name')) // true,实例person9中包含name属性
console.log('name' in person9) // true,经过person9能够访问到name属性
console.log(person10.name); // 'Nicholas',来自原型
console.log(person10.hasOwnProperty('name')) // false,实例person10中不含name属性
console.log('name' in person10) // true,经过person10能够访问到name属性
delete person9 'name'
console.log(person9.name); // 'Nicholas',来自原型
console.log(person9.hasOwnProperty('name')) // false,实例person9中不含name属性
console.log('name' in person9) // true,经过person9能够访问到name属性
  • 同时使用 hasOwnProperty 和 in,判断属性存在于 对象 or 原型
function hasPrototypeProperty(object, name) {
  return !object.hasOwnProperty(name) && name in object
}
var person11 = new PersonIn()
console.log(hasPrototypeProperty(person11, 'name')) // true,!false && true
person11.name = 'Greg'
console.log(hasPrototypeProperty(person11, 'name')) // false,!true && true
  • for-in 循环使用 in:返回全部可以经过对象访问的、可枚举的属性(不管来自实例仍是原型),屏蔽了原型中不可枚举的属性([[Enumerable]]为 false 的属性,如 constructor 和 prototype)
for (var attr in person11) {
  console.log(`${attr}:${person11[attr]}`)
  /*  
    name:Greg
    age:29
    job:Software Engineer
    sayName:function () {
      console.log(this.name)
    } 
  */
}
  • Object.keys():接收一个对象做为参数,返回该对象上(仅该对象自身)可枚举的属性的数组
var keys = Object.keys(PersonIn.prototype) // 原型对象的全部可枚举属性
console.log(keys) // [ 'name', 'age', 'job', 'sayName' ]
var person12 = new PersonIn()
person12.name = 'Bob'
person12.age = 31
var p12keys = Object.keys(person12) // person12的全部可枚举属性
console.log(p12keys) // [ 'name', 'age' ]
  • Object.getOwnPropertyNames():获取该对象上(仅该对象自身)全部属性的数组,不管是否可枚举
var keys = Object.getOwnPropertyNames(PersonIn.prototype) // 原型对象的全部属性,包含不可枚举
console.log(keys) // [ 'constructor', 'name', 'age', 'job', 'sayName' ],原型对象都包含constructor属性,指向构造函数
var p12keys = Object.getOwnPropertyNames(person12) // person12的全部属性,包含不可枚举
console.log(p12keys) // [ 'name', 'age' ]

更简单的原型语法

  • 包含全部属性和方法新对象字面量来重写整个原型对象
function PersonLiteral() {}
PersonLiteral.prototype = {
  name: 'Nicholas',
  age: 29,
  job: 'Software Engineer',
  sayName: function () {
    console.log(this.name)
  },
}
  • 将构造函数的 prototype 属性设置为一个以对象字面量形式建立新对象,其 constructor 属性再也不指向原构造函数,而是
    新对象的 constructor 属性,即 Object 构造函数
var friend = new PersonLiteral()
console.log(friend instanceof Object) // true,friend是Object的实例
console.log(friend instanceof PersonLiteral) // true,friend是PersonLiteral的实例
console.log(friend.constructor === PersonLiteral) // false,constructor属性变成了新对象——即对象字面量的constructor
console.log(friend.constructor === Object) // true,新对象的constructor指向Object
  • 能够在对象字面量里设置 constructor 属性,让其指向原构造函数
  • 这样设置 constructor 属性属于“直接在对象上定义的属性”,会致使 PersonLiteral2.prototype 的 constructor 属性的[[Enumerable]]为 true,能够被循环返回
function PersonLiteral2() {}
PersonLiteral2.prototype = {
  constructor: PersonLiteral2, // 直接在对象上定义constructor,指向原构造函数
  name: 'Nicholas',
  age: 29,
  job: 'Software Engineer',
  sayName: function () {
    console.log(this.name)
  },
}
var friend2 = new PersonLiteral2()
console.log(friend2.constructor === PersonLiteral2) // true,constructor再次指向原构造函数
console.log(friend2.constructor === Object) // false
var keys = Object.keys(PersonLiteral2.prototype)
console.log(keys) // [ 'constructor', 'name', 'age', 'job', 'sayName' ],由于constructor是“直接在对象上定义的属性”
  • 用 Object.defineProperty()修改对象字面量中 constructor 属性的特性,以兼容 ES5 的 javascript 引擎
Object.defineProperty(PersonLiteral2.prototype, 'constructor', {
  enumerable: false,
  value: PersonLiteral2,
})
var keys = Object.keys(PersonLiteral2.prototype)
console.log(keys) // [ 'name', 'age', 'job', 'sayName' ],constructor的enumerable已被设置为false

原型的动态性

  • 对原型对象所作的任何修改都当即从实例上反映出来,即便先建立实例后修改原型
function Person4() {}
var friend3 = new Person4()
Person4.prototype.sayHi = function () {
  console.log('Hi')
}
friend3.sayHi() // 'Hi',先在friend3实例中搜索sayHi属性,没有找到则继续找原型对象
  • 重写整个原型对象,会切断构造函数与最初原型之间的联系,而实例的[[Prototype]]指针指向最初的原型对象
Person4.prototype = {
  constructor: Person4,
  name: 'Nicholas',
  age: 29,
  job: 'Software Engineer',
  sayName: function () {
    console.log(this.name)
  },
}
console.log(friend3.__proto__) // Person4 { sayHi: [Function] },最初的原型对象
console.log(friend3.__proto__ === Person4.prototype) // false,实例的__proto__指向最初的原型对象,重写整个原型切断了构造函数与最初原型之间的联系
friend3.sayName() // error:friend3.sayName is not a function

原生对象的原型

  • 全部原生的引用类型(Array、Object、String...),都是用原型模式建立的,在其构造函数的原型上定义了方法
console.log(Array.prototype) // 在浏览器中查看Array的原型对象,包含sort()等方法
console.log(String.prototype) // 在浏览器中查看Array的原型对象,包含substring()等方法
  • 能够像修改自定义对象的原型同样,修改原生对象的原型,添加或删除方法
  • 不推荐修改原生对象的原型,可能会引发冲突或重写原生方法
String.prototype.startsWith = function (text) {
  // 给String的原型对象添加startsWith方法
  return this.indexOf(text) === 0
}
var msg = 'Hello World'
console.log(msg.startsWith('Hello')) // true
console.log(msg.startsWith('World')) // false
delete String.prototype.startsWith
console.log(msg.startsWith('Hello')) // error

原型对象的问题

  • 原型模式最大的问题是由其共享的本性致使的,尤为对于包含引用类型的属性,对实例的数组、对象等引用类型的属性进行增删改而非从新定义时,会对原型的引用类型属性形成影响
function PersonProblem() {}
PersonProblem.prototype = {
  constructor: PersonProblem,
  name: 'Nicholas',
  age: 29,
  job: 'Software Engineer',
  friends: ['Shelby', 'Court'],
  sayName: function () {
    console.log(this.name)
  },
}
var person13 = new PersonProblem()
var person14 = new PersonProblem()
person13.name = 'Greg' // 从新定义,在实例中屏蔽原型的属性
person13.friends.push('Van') // 非从新定义,而是向原型的数组中添加一个字符串
console.log(person13.name) // 'Greg',从实例得到
console.log(person14.name) // 'Nicholas',从原型中得到
console.log(person13.friends) // [ 'Shelby', 'Court', 'Van' ],从原型中得到
console.log(person14.friends) // [ 'Shelby', 'Court', 'Van' ],从原型中得到
console.log(person13.friends === person14.friends) // true
var person15 = new PersonProblem()
person15.friends = [] // 从新定义,在实例中屏蔽原型的属性
console.log(person15.friends) // [],从实例得到
console.log(person13.friends) // [ 'Shelby', 'Court', 'Van' ],从原型中得到
console.log(person14.friends) // [ 'Shelby', 'Court', 'Van' ],从原型中得到

组合使用构造函数模式和原型模式

  • 构造函数模式用于定义实例属性,原型模式用域定义方法共享的属性
  • 每一个实例都有本身的一份实例属性的副本,同时共享着对方法的引用,最大限度节省内存
  • 该模式是目前 ECMAScript 中使用最普遍、认同度最高的建立自定义类型的方法
function PersonMix(name, age, job) {
  // 在构造函数定义实例属性
  this.name = name
  this.age = age
  this.job = job
  this.friends = ['Shelby', 'Court']
}
PersonMix.prototype = {
  // 在原型定义方法和共享的属性
  constructor: PersonMix, // 直接在对象上定义constructor属性,指向构造函数,[[Enumerable]]为true
  sayName: function () {
    console.log(this.name)
  },
}
var person16 = new PersonMix('Nicholas', 29, 'Software Engineer')
var person17 = new PersonMix('Greg', 27, 'Doctor')
person16.friends.push('Van') // 仅向person16实例自己的friends数组push数据
console.log(person16.friends) // [ 'Shelby', 'Court', 'Van' ]
console.log(person17.friends) // [ 'Shelby', 'Court' ]
console.log(person16.friends === person17.friends) // false,person16实例的friends数组push了数据
console.log(person16.sayName === person17.sayName) // true,共享方法sayName

动态原型模式

  • 将全部信息(属性、方法)都封装在构造函数中,经过检查某个方法是否有效,来决定是否须要初始化原型
function PersonDynamic(name, age, job) {
  // 属性
  this.name = name
  this.age = age
  this.job = job
  // 方法:①只有方法不存在时才添加到原型;②只在初次调用构造函数时执行;③会当即在实例中体现
  if (typeof this.sayName !== 'function') {
    PersonDynamic.prototype.sayName = function () {
      console.log(this.name)
    }
  }
}
var person18 = new PersonDynamic('Nicholas', 29, 'Software Engineer')
person18.sayName() // 'Nicholas'
console.log(person18 instanceof PersonDynamic) // true,person18是PersonDynamic的实例
  • 只有在方法不存在时,才会将方法添加到原型
  • 只在初次调用构造函数时才会执行,此后原型已经完成初始化
  • 对原型所作的修改,会当即在实例中体现
  • 原型的动态性同理,若是已经建立了实例后再用对象字面量重写原型,会切断实例与新原型之间的联系,致使修改无效
PersonDynamic.prototype = {
  newName:
    typeof this.newName !== 'function'
      ? function () {
          console.log('prototype:', this.name)
        }
      : this.newName,
}
person18.newName() // error,person18指向最初的原型,没有newName方法
var person19 = new PersonDynamic('Greg', 27, 'Doctor') // person19是重写原型后建立的实例
person19.newName() // prototype: Greg
person19.sayName() // Greg

console.log(person18 instanceof PersonDynamic) // false,person18不是重写原型后的PersonDynamic的实例,person18指向最初的原型
console.log(person19 instanceof PersonDynamic) // true,person19是重写原型后的PersonDynamic的实例

寄生构造函数模式

  • 构造函数仅封装建立对象的代码,在构造函数内部用原生引用类型建立新对象,通过操做后再返回这个新对象
  • 构造函数内部跟工厂模式如出一辙,调用时用 new 操做符,相似于工厂模式与构造函数模式的结合体
function PersonParasitic(name, age, job) {
  var o = new Object() // 用原生引用类型建立对象
  o.name = name // 添加值
  o.age = age // 添加值
  o.job = job // 添加值
  // 添加方法
  o.sayName = function () {
    console.log(this.name)
  }
  return o
}
var person20 = new PersonParasitic('Nicholas', 29, 'Software Engineer')
person20.sayName() // 'Nicholas'
  • 寄生构造函数模式建立的实例,与构造函数或构造函数的原型没有关联,不能依赖 instanceof 肯定对象类型
  • 若是能够用其余模式,不建议该模式建立自定义对象
function SpecialArray() {
  var values = new Array() // 用原生引用类型建立数组
  values.push.apply(values, arguments) // 添加值
  // 添加方法
  values.toPipedString = function () {
    return this.join('|')
  }
  return values // 返回通过操做后的数组
}
var colors = new SpecialArray('red', 'blue', 'green')
console.log(colors.toPipedString()) // red|blue|green
console.log(SpecialArray.prototype) // SpecialArray{},构造函数的原型对象
console.log(colors.__proto__) // [],构造函数内部经过new Array()从新初始化,其原型对象是原生对象Array
console.log(SpecialArray.prototype === colors.__proto__) // false,两者无关联
console.log(colors instanceof SpecialArray) // false,两者无关联

稳妥构造函数模式

  • 与计生构造函数相似相似,在构造函数中用原生引用类型建立新对象,但没有公共属性其方法也不引用 this不使用 new 调用构造函数
  • 除了构造函数内部的方法,没有其余办法可访问到构造函数内部的原始数据(即使有其余代码给对象添加方法或属性),该模式适合在安全环境下使用
  • 寄生构造函数模式,该模式下建立的实例,与构造函数或构造函数的原型没有关联,不能依赖 instanceof 肯定对象类型
function PersonSafe(name, age, job) {
  var o = new Object() // 用原生引用类型建立对象
  o.sayName = function () {
    console.log(name)
  }
  return o
}
var person21 = new PersonParasitic('Nicholas', 29, 'Software Engineer')
person21.sayName() // 'Nicholas'

总结 & 问点

建立对象 过程 缺点
Object 构造函数 1.建立 Objecty 实例 2.添加属性和方法 同一接口建立多个对象,大量重复代码
对象字面量 直接建立包含属性和方法的对象 同一接口建立多个对象,大量重复代码
工厂模式 1.用函数封装建立 Object 实例的过程(添加属性和方法、返回该实例对象) 2.调用该函数 没有解决对象识别问题,即怎样知道一个对象的类型
构造函数模式 1.构造函数封装(不显示的建立对象、属性和方法赋给 this 对象、无 return) 2.new 调用构造函数 每一个实例从新建立方法,机制相同的 Function 对象被屡次实例化
原型模式 1.构造函数封装(空的,无属性和方法) 2.原型对象上添加属性和方法 3.new 调用构造函数 对实例的引用类型属性修改而非从新定义时,会对原型的引用类型属性形成影响
组合使用构造函数模式和原型模式 1.构造函数封装实例属性 2.原型模式定义方法和共享属性 3.new 调用构造函数 无明显缺点,目前最普遍认同度最高,是默认模式
动态原型模式 1.构造函数封装全部信息(属性、检查某个方法是否有效来决定是否初始化原型) 2.new 调用构造函数 无明显缺点,不要使用对象字面量重写原型
寄生构造函数模式 1.构造函数封装(原生引用类型建立新对象、添加值或方法、返回新对象) 2.new 调用构造函数 实例与构造函数或构造函数的原型没有关联,不能依赖 instanceof 肯定对象类型
稳妥构造函数模式 1.同寄生构造函数模式的构造函数封装(但方法不引用 this) 2.做为普通函数调用构造函数 实例与构造函数或构造函数的原型没有关联,不能依赖 instanceof 肯定对象类型
对象 属性 指向 用法
任何函数 prototype 原型对象 Person.prototype → 构造函数的原型对象
实例、原型 constructor 构造函数 person1.constructor === Person.prototype.constructor === Person
实例 [[Prototype]] 原型对象 person1.__proto__ === Person.prototype(没有标准方式访问[[Prototype]],但可用 __proto__
操做符 含义 用法
new 建立构造函数的实例(四个步骤 var person = new Person()
in 可否经过对象访问到属性(不管属性在实例仍是原型中) console.log('name' in person)
for-in 返回全部能经过对象访问到的、可枚举的属性(不管属性在实例仍是原型中) for(var attr in person){console.log(attr)}
delete 删除实例属性 delete person.name
对象 方法 含义 参数 返回值 用法
Object 对象 getPrototypeOf() 获取实例对象的原型 实例 原型对象 Object.getPrototypeOf('person') === Person.prototype
任何对象 hasOwnProperty() 对象自身(不包括原型)是否含有该属性 属性 true/false console.log(Person.hasOwnProperty('name'))
Object 对象 keys() 获取对象上全部可枚举的属性(仅对象自身) 对象 属性的字符串数组 Object.keys(person)
Object 对象 getOwnPropertyNames() 获取对象上全部属性(仅对象自身,不管是否可枚举) 对象 属性的字符串数组 Object.getOwnPropertyNames(person),原型对象会包含 constructor 属性
  • 建立单个对象有哪些方法?这些方法有什
    么缺点?
  • 工厂模式作出了怎样的优化?该模式有什么缺点?
  • 相比工厂模式,构造函数模式有哪些区别?如何建立其实例?
  • 构造函数在 new 的过程当中都发生了什么?
  • 构造函数建立出的对象,其 construtor 属性指向哪里?这样的对象是哪些构造函数的实例?
  • 相比工厂模式,构造函数有什么优点?
  • 构造函数与普通函数有什么相同点和区别?
  • 自定义对象的方法时,构造函数模式有什么缺点?
  • 用全局函数代替构造函数内部的对象属性的方法,仍有什么缺点?
  • prototype 属性的含义和用法?使用原型对象的好处是什么?
  • 原型对象的 constructor 属性的含义和用法?
  • 用什么方法检测实例是否含有指向原型对象的指针?
  • 构造函数、实例的哪些属性或方法可得到原型对象?如何经过实例得到构造函数?
  • 代码读取对象属性时,经历了怎样的搜索过程?
  • 是否能够经过实例访问和修改原型中的属性值?
  • 在实例中添加与原型的同名属性会怎样?再删除这个实例中的属性呢?
  • 用什么方法检测属性存在与实例 or 原型?
  • 用什么方法获取原型属性的描述符?
  • 单独使用 in 的用法是什么?其和 hasOwnProperty()方法的区别是什么?
  • for-in 的用法是什么?其返回哪些属性屏蔽哪些属性?
  • Object.keys()的和 Object.getOwnPropertyNames()用法分别是什么?
  • Object.getOwnPropertyNames()、Object.leys()、for-in 的区别是什么?
  • 用一个对象字面量的新对象重写整个原型对象时,原型对象的 constructor 指向发生了怎样的改变?
  • 写一段代码,用对象字面量重写构造函数的原型对象,且让原型对象的 constructor 仍指向原构造函数,并保留 construtor 的[[Enumerable]]特性为 false
  • 建立实例后再修改原型的属性,实例会受到影响么?为何?
  • 重写整个原型对象后,构造函数的 prototype 指向哪里?实例的[[Prototype]]属性指向哪里?为何?
  • 原生引用类型的方法是如何建立的?为何不推荐修改原生引用类型的原型?
  • 原型模式的“共享”本性,在修改包含引用类型的属性时,有怎样的问题?
  • ECMAScript 使用最普遍、认同度最高的建立自定义类型的方法是什么?其原理和优点是什么?
  • 动态原型模式是如何初始化原型的?其对原型的修改有哪些注意点?
  • 寄生构造函数模式的原理是什么?为何其建立的实例,与其构造函数或构造函数的原型没有关联?
  • 稳妥构造函数模式的原理是什么?为何该模式比较“稳妥”?
相关文章
相关标签/搜索