关注前端小讴,浏览更多原创技术文章
创建对象
- 创立单个对象:Object 构造函数 和 对象字面量
- 毛病:应用一个接口创立很多对象,产生大量反复代码
相干代码 →
工厂模式
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 等原生构造函数,还能够创立自定义的构造函数
构造函数模式 vs 工厂模式
- 不显式的创建对象
- 间接将属性和办法赋给 this 对象
- 没有 return
function Person(name, age, job) { this.name = name this.age = age this.job = job this.sayName = function () { console.log(this.name) }}var person1 = new Person('Nicholas', 29, 'Software Engineer')var person2 = new Person('Greg', 27, 'Doctor')
- 构造函数用大写字母结尾,创立实例时用
new
操作符 构造函数 new 一个对象后:
- 创立了一个新对象(实例)
- 新对象外部的
[[Prototype]]
个性被赋值为构造函数的prototype
属性(独特指向原型) - 将构造函数的作用域(即 this)赋给新对象
- 执行构造函数中的代码(即:为这个对象增加新属性)
- 返回新对象或非空对象
- 创立的对象(实例)既是 Object 的实例,又是构造函数的实例,其
constructor
属性指向构造函数 - 能够确保自定义构造函数的实例被标识为特定的类型,是构造函数模式胜过工厂模式的中央
console.log(person1.constructor === Person) // true,constructor 属性指向构造函数console.log(person2.constructor === Person) // true,constructor 属性指向构造函数console.log(person1 instanceof Object) // true,person1是Object的实例console.log(person1 instanceof Person) // true,person1是Person的实例console.log(person2 instanceof Object) // true,person2是Object的实例console.log(person2 instanceof Person) // true,person2是Person的实例
- 构造函数也能够应用函数表达式示意,实例化不传参数时,构造函数前面的括号可加可不加
var PersonExpression = function () { // 构造函数的函数表达式 this.name = 'Jake' this.sayName = function () { console.log(this.name) }}var personNoBrackets = new PersonExpression() // 实例化不传参数,可不加括号
构造函数也是函数
- 构造函数与一般函数惟一的区别是调用形式不同:应用
new
操作符调用的就是构造函数,不应用的是一般函数 - 默认状况下,调用函数时的
this
指向 Global
对象(浏览器中指向 window
对象)
var person3 = new Person('Nicholas', 29, 'Software Engineer') // 用构造函数创建对象person3.sayName() // 'Nicholas'Person('Greg', 27, 'Doctor') // 'Greg',不应用new操作符,间接调用global.sayName() // 间接调用函数,this指向Global对象(浏览器中指向window对象)var o = new Object() // 新对象ovar p = new Object() // 新对象pPerson.call(o, 'Kristen', 25, 'Nurse') // 将对象o指定为Person()外部的this值,call()别离传入每个参数Person.apply(p, ['Kristen', 25, 'Nurse']) // 将对象o指定为Person()外部的this值,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
属性,指回与之关联的构造函数
console.log(PersonPrototype.prototype.constructor) // PersonPrototype构造函数,原型对象的constructor属性指向与之关联的构造函数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构造函数console.log(person5.__proto__ === person6.__proto__) // true,共享同一个原型对象
instanceof
查看实例的原型链中,是否蕴含指定构造函数的原型
console.log(person5 instanceof PersonPrototype) // true,person5是PersonPrototype的实例console.log(person5 instanceof Object) // true,person5是Object的实例console.log(PersonPrototype.prototype instanceof Object) // true,所有实例对象和原型对象都是Object的实例
- 原型对象的
isPrototypeOf()
办法,检测实例中是否有指向原型对象的指针
console.log(PersonPrototype.prototype.isPrototypeOf(person5)) // true,person5蕴含指向PersonPrototype的原型对象的指针console.log(PersonPrototype.prototype.isPrototypeOf(person1)) // false,person1不蕴含指向PersonPrototype的原型对象的指针
Object.getPrototypeOf()
办法(参数个别为实例),返回参数的[[Prototype]]
的值(个别为原型对象)
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构造函数
Object.setPrototypeOf()
办法,向实例(参数一)的[[Prototype]]
写入一个新值(参数二),从而重写一个对象的原型继承关系
var biped = { numLegs: 2,}var person = { name: 'Matt',}Object.setPrototypeOf(person, biped)console.log(person.name) // 'Matt'console.log(person.numLegs) // 2console.log(person.__proto__) // { numLegs: 2 },person的[[Prototype]]指针指向biped
- 为防止
Object.setPrototypeOf()
可能重大影响代码性能,可应用Object.create()
创立一个新对象,同时为其指定原型(参数)
var biped2 = { numLegs: 3,}var person = Object.create(biped2)console.log(person.numLegs) // 3console.log(person.__proto__) // { numLegs: 3 },person的[[Prototype]]指针指向biped2
原型层级
代码读取对象属性的搜寻过程:
- 1.搜寻对象实例自身 -> 有属性 → 返回属性值 -> 完结
- 2.对象实例自身无属性 -> 搜寻原型对象 → 有/无属性 → 返回属性值/undefined → 完结
- 能够通过实例拜访原型中属性的值(如 constructor 属性),但无奈通过实例重写原型中属性的值
- 如果增加的实例属性与原型的属性同名,则实例属性屏蔽原型中的属性
var person7 = new PersonPrototype()person7.name = 'Greg'console.log(person7.name) // 'Greg',来自实例console.log(person5.name) // 'Nicholas',来自原型
delete person7.nameconsole.log(person7.name) // 'Nicholas',来自原型
- 应用
hasOwnProperty()
办法,检测属性是否存在于实例中(存在返回 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.nameconsole.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 = 29PersonIn.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
操作符,判断属性存在于实例还是原型
function hasPrototypeProperty(object, name) { return !object.hasOwnProperty(name) && name in object // 不存在于实例 && 能拜访到 → 存在于原型}var person11 = new PersonIn()console.log(hasPrototypeProperty(person11, 'name')) // true,!false && trueperson11.name = 'Greg'console.log(hasPrototypeProperty(person11, 'name')) // false,!true && true
for-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 = 31var 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' ]console.log(Object.getOwnPropertyNames(PersonIn)) // [ 'length', 'name', 'arguments', 'caller', 'prototype' ]
- ES6 新增
Object.getOwnPropertySymbols()
办法,返回对象(本身)所有符号键属性(无论是否可枚举)的数组,参数为该对象
var k1 = Symbol('k1')var k2 = Symbol('k2')var o = { [k1]: 'k1', // 符号作为属性,需应用“计算属性”语法,即[属性名] [k2]: 'k2',}console.log(Object.getOwnPropertySymbols(o)) // [ Symbol(k1), Symbol(k2) ]
属性枚举程序
var k1 = Symbol('k1')var k2 = Symbol('k2')var o = { 1: 1, first: 'first', [k2]: 'sym2', third: 'third', 0: 0,}o[k1] = 'sym1'o[3] = 3o.second = 'second'o[2] = 2console.log(Object.getOwnPropertyNames(o)) // [ '0', '1', '2', '3', 'first', 'third', 'second' ]console.log(Object.getOwnPropertySymbols(o)) // [ Symbol(k2), Symbol(k1) ]
对象迭代
- ES7 新增
Object.values()
和Object.entries()
办法,接管参数对象,别离返回对象值和对象键/值对数组
var o = { foo: 'bar', baz: 1, qux: {},}console.log(Object.values(o)) // [ 'bar', 1, {} ],迭代值console.log(Object.entries(o)) // [ [ 'foo', 'bar' ], [ 'baz', 1 ], [ 'qux', {} ] ],迭代键值对
var o = { qux: {},}console.log(Object.values(o)) // [ {} ]console.log(Object.entries(o)) // [ [ 'qux', {} ] ]console.log(Object.values(o)[0] === o.qux) // true,浅复制,复制对象的援用console.log(Object.entries(o)[0][1] === o.qux) // true,浅复制,复制对象的援用
var sym = Symbol()var o = { [sym]: 'foo', // 符号属性}console.log(Object.values(o)) // [],符号属性被疏忽console.log(Object.entries(o)) // [],符号属性被疏忽
其余原型语法
- 用蕴含所有属性和办法的新对象字面量来重写整个原型对象
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属性变成了新对象——即对象字面量的constructorconsole.log(friend.constructor === Object) // true,新对象的constructor指向Object构造函数
- 能够在对象字面量里设置
constructor
属性,让其指向原构造函数 - 这样设置
constructor
属性属于间接在对象上定义的属性,会导致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) // falseconsole.log(Object.keys(PersonLiteral2.prototype)) // [ 'constructor', 'name', 'age', 'job', 'sayName' ],因为constructor是“间接在对象上定义的属性”,可被枚举进去
- 不在对象字面量内设置
constructor
属性,而用Object.defineProperty()
批改对象字面量中constructor
属性的个性,以兼容 JavaScript 引擎
Object.defineProperty(PersonLiteral2.prototype, 'constructor', { enumerable: false, value: PersonLiteral2,})console.log(Object.keys(PersonLiteral2.prototype)) // [ 'name', 'age', 'job', 'sayName' ],constructor的enumerable已被设置为false
原型的动态性
- 对原型对象所做的任何批改都立刻从实例上反映进去,即便先创立实例后批改原型
function Person4() {}var friend3 = new Person4() // 先创立实例Person4.prototype.sayHi = function () { // 后批改原型对象 console.log('Hi')}friend3.sayHi() // 'Hi',实例受影响,实例指向原型
- 重写整个原型,会切断构造函数与最后原型之间的分割,(重写原型前创立的)实例的[[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] },friend3在重写原型前创立,[[Prototype]]指向最后的原型对象console.log(friend3.__proto__ === Person4.prototype) // false,重写整个原型切断了构造函数与最后原型之间的分割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')) // trueconsole.log(msg.startsWith('World')) // falsedelete String.prototype.startsWithconsole.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) // truevar person15 = new PersonProblem()person15.friends = [] // 从新定义,在实例中屏蔽原型的属性console.log(person15.friends) // [],从实例取得console.log(person13.friends) // [ 'Shelby', 'Court', 'Van' ],从原型中取得console.log(person14.friends) // [ 'Shelby', 'Court', 'Van' ],从原型中取得
总结 & 问点
创建对象 | 过程 | 毛病 |
---|
Object 构造函数 | 1.创立 Objecty 实例 2.增加属性和办法 | 同一接口创立多个对象,大量反复代码 |
对象字面量 | 间接创立蕴含属性和办法的对象 | 同一接口创立多个对象,大量反复代码 |
工厂模式 | 1.用函数封装创立 Object 实例的过程(增加属性和办法、返回该实例对象) 2.调用该函数 | 没有解决对象辨认问题,即怎么晓得一个对象的类型 |
构造函数模式 | 1.构造函数封装(不显示的创建对象、属性和办法赋给 this 对象、无 return) 2.new 调用构造函数 | 每个实例从新创立办法,机制雷同的 Function 对象被屡次实例化 |
原型模式 | 1.构造函数封装(空的,无属性和办法) 2.原型对象上增加属性和办法 3.new 调用构造函数 | 对实例来自原型的援用类型属性批改而非从新定义时,会对原型造成影响 |
对象 | 属性 | 默认指向 | 用法 |
---|
任何函数 | prototype | 原型对象 | Person.prototype → 构造函数的原型对象 |
实例、原型 | constructor | 构造函数 | person1.constructor === Person.prototype.constructor === Person |
实例 | [[Prototype]] | 原型对象 | person1.__proto__ === Person.prototype (没有规范形式拜访[[Prototype]] ,但可用 __proto__ ) |
操作符 | 含意 | 用法 |
---|
new | 创立构造函数的实例(四个步骤) | var person = new Person() |
delete | 删除实例属性 | delete person.name |
in | 是否通过对象拜访到属性(无论属性在实例还是原型中) | console.log('name' in person) |
for-in | 返回所有能通过对象拜访到的、可枚举的属性(无论属性在实例还是原型中) | for(var attr in person){console.log(attr)} |
办法 | 含意 | 参数 | 返回值 |
---|
isPrototypeOf() | 实例是否有指向原型对象的指针 | 实例 | true/false |
Object.getPrototypeOf() | 获取实例[[Prototype]] 的值 | 实例 | 原型对象 |
Object.setPrototypeOf() | 向实例的[[Prototype]] 写入新值(指定原型) | ① 实例 ② 指定原型 | |
Object.create() | 创立一个新对象,同时为其指定原型 | 指定原型 | |
hasOwnProperty() | 属性是否存在于实例中(非原型中) | 属性 | true/false |
Object.keys() | 获取对象(本身)所有可枚举的属性 | 对象 | 属性的字符串数组 |
Object.getOwnPropertyNames() | 获取对象(本身)所有属性(无论是否可枚举) | 对象 | 属性的字符串数组(原型对象蕴含 constructor 属性) |
Object.getOwnPropertySymbols() | 获取对象(本身)所有符号键属性(无论是否可枚举) | 对象 | 属性的符号键数组(原型对象蕴含 constructor 属性) |
办法/操作符 | 枚举程序 |
---|
for-in | 枚举程序不确定,取决于浏览器的 JS 引擎 |
Object.keys() | 枚举程序不确定,取决于浏览器的 JS 引擎 |
Object.getOwnPropertyNames() | 枚举程序确定:先以升序枚举数值键,后以插入程序枚举字符串和符号键 |
Object.getOwnPropertySymbols() | 枚举程序确定:先以升序枚举数值键,后以插入程序枚举字符串和符号键 |
- 创立单个对象有哪些办法?这些办法有什么毛病?
- 工厂模式做出了怎么的优化?该模式有什么毛病?
- 相比工厂模式,构造函数模式有哪些区别和劣势?其在 new 的过程中都产生了什么?
- 构造函数创立出的对象,其 construtor 属性指向哪里?这样的对象是哪些构造函数的实例?
- 相比一般函数,构造函数有什么相同点和区别?
- 构造函数模式有什么毛病?用全局函数代替构造函数外部对象的办法,仍有什么毛病?
- 函数的 prototype 属性是什么?应用原型对象的益处是什么?如何了解原型对象的 constructor 属性?
- 构造函数、实例、原型对象之间,别离能够用什么形式互相获取?用什么办法检测实例是否含有指向原型对象的指针?
- Object.getPrototypeOf()、Object.setPrototypeOf()、Object.create()别离的含意和用法是什么?
- 代码读取对象属性时,经验了怎么的搜寻过程?是否能够通过实例拜访和批改原型中的属性值?
- 在实例中增加与原型的同名属性会怎么?再删除这个实例中新增的属性呢?
- 独自应用 in 操作符的含意是什么?其和 hasOwnProperty()办法的区别是什么?
- 请写一段代码,判断某个属性存在于实例还是原型
- for-in 的用法是什么?其返回哪些属性屏蔽哪些属性?
- Object.keys()、Object.getOwnPropertyNames()、Object.getOwnPropertySymbols()、Object.values()、Object.entries()的用法别离是什么?
- for-in、Object.keys()、Object.getOwnPropertyNames()、Object.getOwnPropertySymbols()在属性枚举时的程序有什么区别?
- 用一个对象字面量的新对象重写整个原型对象时,原型对象的 constructor 指向产生了怎么的扭转?
- 写一段代码,用对象字面量重写构造函数的原型对象,且原型对象的 constructor 仍指向原构造函数,并保留 construtor 属性“不可被枚举”的个性
- 创立实例后再批改原型的属性,实例会受到影响么?为什么?
- 重写整个原型对象后,构造函数的 prototype 指向哪里?重写前创立的实例的[[Prototype]]属性指向哪里?为什么?
- 原生援用类型的办法是如何创立的?为什么不举荐批改原生援用类型的原型?
- 原型模式的“共享”本色,在批改蕴含援用类型的属性时,会产生怎么的问题?