1.value 值
2.writable 能否被赋值
3.enumerable 能否被枚举 for in Object.keys
4.configurable 能否被删除或者改变属性值
var a ={a:1}
Object.getOwnPropertyDescriptor(a,'a') //{configurable: true,enumerable: true,value: 1,writable: true}
Object.defineProperty(a,'a',{value:5,configurable: false,enumerable: false,writable: false})
1.Getter 取值
2.setter 赋值
3.enumerable 能否被枚举 for in
4.configurable 能否被删除或者改变属性值
let o = {
b: 1,
get a() {
return this.b;
},
set a(i) {
this.b = i;
},
};
console.log(o.a); // 1
o.a = 5;
console.log(o.a); // 5
var o = { b: 1 };
Object.defineProperty(o, 'a', {
set: function (value) {
this.b = value + 100;
},
get: function () {
return this.b;
},
});
console.warn(o.a); // 6
o.a = 6;
console.warn(o.a); // 106
对象的key只能是字符串 和 symbol
var id = Symbol();
var o = { name: 'a', age: 18, [id]: 1 }; // 对象的key 键名是数值
console.warn(o[id]); // 1
// 获取key string
console.log(Object.getOwnPropertyNames(o)); //[ 'name', 'age' ]
console.log(Object.keys(o)); //[ 'name', 'age' ]
// 获取key Symbol
console.log(Object.getOwnPropertySymbols(o)); // [ Symbol() ]
// 获取所以属性
console.log(Object.getOwnPropertyNames(o).concat(Object.getOwnPropertySymbols(o))); // [ 'name', 'age', Symbol() ]
console.log(Reflect.ownKeys(o)); // [ 'name', 'age', Symbol() ]
// 获取属性
console.log(Object.getOwnPropertyDescriptor(o, 'name')); // { value: 'a', writable: true, enumerable: true, configurable: true }
console.log(Object.getOwnPropertyDescriptors(o));
// {
// name: { value: 'a', writable: true, enumerable: true, configurable: true },
// age: { value: 18, writable: true, enumerable: true, configurable: true },
// [Symbol()]: { value: 1, writable: true, enumerable: true, configurable: true }
// }
for (let i in o) {
console.log(i); // name age
}
console.log(o.hasOwnProperty('name')); // true
console.log(o.hasOwnProperty(id)); // true
缺点:产生大量重复代码
var id = Symbol();
var o = new Object({ name: 'a', age: 18, [id]: 1 }); // 对象的key 键名是数值
console.warn(o[id]); // 1
// 获取key string
console.log(Object.getOwnPropertyNames(o)); //[ 'name', 'age' ]
console.log(Object.keys(o)); //[ 'name', 'age' ]
// 获取key Symbol
console.log(Object.getOwnPropertySymbols(o)); // [ Symbol() ]
// 获取所以属性
console.log(Object.getOwnPropertyNames(o).concat(Object.getOwnPropertySymbols(o))); // [ 'name', 'age', Symbol() ]
console.log(Reflect.ownKeys(o)); // [ 'name', 'age', Symbol() ]
// 获取属性
console.log(Object.getOwnPropertyDescriptor(o, 'name')); // { value: 'a', writable: true, enumerable: true, configurable: true }
console.log(Object.getOwnPropertyDescriptors(o));
// {
// name: { value: 'a', writable: true, enumerable: true, configurable: true },
// age: { value: 18, writable: true, enumerable: true, configurable: true },
// [Symbol()]: { value: 1, writable: true, enumerable: true, configurable: true }
// }
for (let i in o) {
console.log(i); // name age
}
console.log(o.hasOwnProperty('name')); // true
console.log(o.hasOwnProperty(id)); // true
缺点:产生大量重复代码
使用现有的原型来创建对象
var id = Symbol();
var o = Object.create({ name: 'a', age: 18, [id]: 1 }); // 对象的key 键名是数值
console.warn(o[id]); // 1
// 获取key string
console.log(Object.getOwnPropertyNames(o)); //[ ]
console.log(Object.keys(o)); //[ ]
// 获取key Symbol
console.log(Object.getOwnPropertySymbols(o)); // [ ]
// 获取所以属性
console.log(Object.getOwnPropertyNames(o).concat(Object.getOwnPropertySymbols(o))); // [ ]
console.log(Reflect.ownKeys(o)); // [ ]
// 获取属性
console.log(Object.getOwnPropertyDescriptor(o, 'name')); // undefined
console.log(Object.getOwnPropertyDescriptors(o)); // {}
for (let i in o) {
console.log(i); // name age
}
console.log(o.hasOwnProperty('name')); // false
console.log(o.hasOwnProperty(id)); // false
function createPerson(name,age) {
var o = new Object()
o.name = name;
o.age = age;
o.sayName=function() {
console.log(this.name)
}
return o
}
var p1 = createPerson('a',18)
var p2 = createPerson('b',18)
console.log(p1 instanceof Object) // true
console.log(p2 instanceof Object) // true
console.log(p2.__proto__ === p1.__proto__)
优点:能一次创建多个对象
缺点:创建的对象没法区别 所有实例的原型都指向同一个对象
function Person(name,age) {
this.name = name;
this.age = age;
this.sayName = function() {
console.log(this.name)
}
}
var p3 = new Person('a',18)
p3.sayName() // q
p3.name = 'aa'
p3.sayName() // qq
var p4 = new Person('b',18)
p4.sayName() // b
console.log(p3 instanceof Person) // true
console.log(p4 instanceof Person) // true
console.log(p4.__proto__ === p3.__proto__)
优点:显式的创建了对象 可以识别为一种特点类型
缺点:每次创建对象时方法都要被创建一次
function Person1() {
}
Person1.prototype.name = 'a'
Person1.age = '18'
Person1.sayName = function() {
console.log(this.name)
}
var p5 = new Person('a',18)
p5.sayName() // a
p5.name = 'aa'
p5.sayName() // aa
var p6 = new Person('b',18)
p6.sayName() // b
console.log(p5 instanceof Person) // true
console.log(p5 instanceof Person) // true
console.log(p6.__proto__ === p6.__proto__)
优点:方法不会重建
缺点:所有属性和方法都共享 不能初始化参数
/**
* 原型模式优化
* 优点:方法不会重建 更简单
* 缺点:所有属性和方法都共享 不能初始化参数 完全重写了原型 ,导致constructor指向不对,要明确指向constructor
* */
function Person11() {
}
Person11.prototype = {
constructor: Person11, // 完全重写了原型 ,导致constructor指向不对,要明确指向constructor
name : 'a',
age: 18,
sayName:function() {
console.log(this.name)
}
}
var p55 = new Person11()
p55.sayName() // a
p55.name = 'aa'
p55.sayName() // aa
var p66 = new Person1()
p66.sayName() // a
console.log(p55 instanceof Person11) // true
console.log(p55 instanceof Person11) // true
console.log(p66.__proto__ === p55.__proto__) // false
console.log(Person11.prototype.constructor === Person11) // true
就是把构造模式和原型模式组合起来,私有变量放在构造函数里面,公用方法放在原型链里面
function Person2(name,age) {
this.name = name;
this.age = age
}
Person2.prototype.sayName = function() {
console.log(this.name)
}
var p7 = new Person2('a',18)
p7.sayName() // a
p7.name = 'aa'
p7.sayName() // aa
var p8 = new Person2('b',18)
p8.sayName() // b
console.log(p7 instanceof Person2) // true
console.log(p7 instanceof Person2) // true
console.log(p8.__proto__ === p7.__proto__)
优点:有私有变量 和 共有 方面
就是把组合模式改变下 原型链定义的方法放在构造函数里面,如果实例没有这个方法 就定义这把这个方法放在原型里,
相比组合模式 封装更多
function Person3(name,age) {
this.name = name;
this.age = age
if(typeof this.sayName !== 'function') {
Person3.prototype.sayName = function() {
console.log(this.name)
}
// 或者
// Person3.prototype = {
// constructor: Person3, // 对象字面量会导致原型被覆盖 实例的原型指向错误 需要明确指向constructor
// sayName: function() {
// console.log(this.name)
// }
// }
}
}
var p7 = new Person3('a',18)
p7.sayName() // a
p7.name = 'aa'
p7.sayName() // aa
var p8 = new Person3('b',18)
p8.sayName() // b
console.log(p7 instanceof Person3) // true 改为对象字面量 变为 false
console.log(p7 instanceof Person3) // true 改为对象字面量 变为 false
console.log(p7.__proto__)
console.log(p8.__proto__ === p7.__proto__) // true 改为对象字面量 变为 false
就是把工厂方法写在构造函数里面 和工厂方法的区别是实例通过new
function Person4(name,age) {
var o = new Object()
o.name = name;
o.age = age;
o.sayName=function() {
console.log(this.name)
}
return o
}
var p7 = new Person4('a',18)
p7.sayName() // a
p7.name = 'aa'
p7.sayName() // aa
var p8 = new Person4('b',18)
p8.sayName() // b
console.log(p7 instanceof Person4) // false
console.log(p7 instanceof Object) // true
console.log(p8.__proto__ === p7.__proto__) // true
用途:对某个类型进行属性增强时 出于安全考虑不能直接修改原型,可以考虑这种方式
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"
不用this 没有公共属性 不用new
优点:安全
function person9(name){
var o = new Object();
o.sayName = function(){
console.log(name);
};
return o;
}
var person1 = person9('kevin');
person1.sayName(); // kevin
person1.name = "daisy";
person1.sayName(); // kevin
console.log(person1.name); // daisy
工厂模式 | 构造函数模式 | 原型模式 | 组合模式 | 动态模式 | 寄生构造函数 | 稳妥构造函数 | |
---|---|---|---|---|---|---|---|
描述 | 一个function 里面返回一个对象 | 一个构造函数 this new()声明 | Prototype | 构造函数(属性)+原型模式(方法) | 组合模式改进 把方法定义放在构造函数里面 | 就是工厂函数 只是用new声明 | 就是工厂函数 只是不用this 没有共有属性 只有方法 返回传入的值 |
优点 | 简单 能一次创建多个对象实例 | 显式的创建一种类型 | 方法只创建一次 | 既有共有方法也有私有属性 | 封装更好 | ||
缺点 | 创建的对象没法区分类型 都指向object | 方法每次创建都要创建一次 | 属性和方法公用 不能初始化属性 | ||||
用途 | 类型属性增强 | 安全场景 |
获取属性
Object.getOwnPropertyDescriptor(o, ‘name’) 获取o.name属性 不能获取原型链上的
Object.getOwnPropertyDescriptors(o) 获取o全部属性 不能获取原型链上的
枚举
o.hasOwnProperty(‘name’) 判断是否是原生属性 不能获取到原型链上的
var id = Symbol();
var o = { name: 'a', age: 18, [id]: 1 }; // 对象的key 键名是数值
co[id]; // 1
// 获取key string
Object.getOwnPropertyNames(o); //[ 'name', 'age' ]
Object.keys(o); //[ 'name', 'age' ]
// 获取key Symbol
Object.getOwnPropertySymbols(o); // [ Symbol() ]
// 获取所以属性
Object.getOwnPropertyNames(o).concat(Object.getOwnPropertySymbols(o)); // [ 'name', 'age', Symbol() ]
Reflect.ownKeys(o); // [ 'name', 'age', Symbol() ]
// 获取属性
Object.getOwnPropertyDescriptor(o, 'name'); // { value: 'a', writable: true, enumerable: true, configurable: true }
Object.getOwnPropertyDescriptors(o);
// {
// name: { value: 'a', writable: true, enumerable: true, configurable: true },
// age: { value: 18, writable: true, enumerable: true, configurable: true },
// [Symbol()]: { value: 1, writable: true, enumerable: true, configurable: true }
// }
var b = Symbol('b');
function person() {
this.name = 'a';
this.age = 18;
this[b] = 2;
}
person.prototype[id] = 1;
person.prototype.sex = 'girl';
var p = new person();
p[id]; // 1
// 获取key string
Object.getOwnPropertyNames(p); //[ 'name', 'age' ]
Object.keys(p); //[ 'name', 'age' ]
// 获取key Symbol
Object.getOwnPropertySymbols(p); // [ Symbol(b) ]
// 获取所以属性
Object.getOwnPropertyNames(p).concat(Object.getOwnPropertySymbols(p)); // [ 'name', 'age', Symbol(b) ]
Reflect.ownKeys(p); // [ 'name', 'age', Symbol(b) ]
// 获取属性
Object.getOwnPropertyDescriptor(p, 'name'); // { value: 'a', writable: true, enumerable: true, configurable: true }
Object.getOwnPropertyDescriptors(p);
// {
// name: { value: 'a', writable: true, enumerable: true, configurable: true },
// age: { value: 18, writable: true, enumerable: true, configurable: true }
// [Symbol(b)]: { value: 2, writable: true, enumerable: true, configurable: true }
// }
for (let i in o) {
console.log(i); // name age
}
for (let i in p) {
console.log(i); // name age sex
}
console.log(o.hasOwnProperty('name')); // true
console.log(p.hasOwnProperty('name')); // true
console.log(o.hasOwnProperty(id)); // true
console.log(p.hasOwnProperty(id)); // false
console.log(p.hasOwnProperty(b)); // true
定义:每个对象创建时都会与之关联一个原型对象,每个对象都会从原型继承属性。
对象是通过构造函数创建的 这个构造函数有一个属性prototype是一个对象,这个对象就是原型。原型包括了一些公用的属性和方法。构造函数创建的对象会从这个原型中继承这个属性。当找一个属性或者方法时在对象找不到会沿着原型链找。
function Person(){
}
Person.prototype.name = 'a' // 这里的protype就是原型
var p = new Person()
console.log(p.name) // a // p对象会从Person.protype原型那里继承name属性
console.log(Person.prototype) // {name:'a'} prototype指向原型对象
console.log(Object.getPrototypeOf(p) ) // {name:'a'} Object.getPrototypeOf 获取原型对象
console.log(p.__proto__ === Person.prototype) // true 对象的___proto__指向对象的原型
console.log(Object.getPrototypeOf(p)=== Person.prototype ) //true
console.log(Person === Person.prototype.constructor) // true 原型的constructor指向构造函数
console.log(Person.prototype.__proto__) // [Object: null prototype] {} 原型的原型是object
console.log(Person.prototype.__proto__.__proto__) // null 原型链的尽头是null
console.log(Person.prototype.__proto__ === Object.prototype) // true 原型的原型是object的原型
构造函数 实例 原型 的关系如上图:
__proto__
指向原型对象 Object.getPrototypeOf(实例) 获取实例的原型对象定义:就是相互关联的原型 形成的一个链条 相互之间通过__proto__
连接。原型对象是通过obejct构造函数创建的,原型的原型是object的原型对象, 原型链的尽头时null。
首先创建了一个空对象
然后设置空对象的__proto__
作为构造函数的原型prototype
然后让构造函数的this指向这个对象,允许构造函数
判断构造函数的返回值 如果是引用类型就返回这个引用类型,如果不是返回创建的对象
function createNew(fn) {
const obj = {};
obj.__proto__ = fn.prototype;
const result = fn.apply(obj,[...arguments].slice(1));
return typeof result === 'object' ? result:obj
}
function Person(name,age) {
this.name = name;
this.age = age;
}
Person.prototype.sayName = function() {
console.log(this.name)
}
Person.prototype.sayAge = function() {
console.log(this.age)
}
const p = createNew(Person,'aa',18)
p.sayName() // aa
p.sayAge() // 18xxxxxxxxxx funcfunction createNew(fn) { const obj = {}; obj.__proto__ = fn.prototype; const result = fn.apply(obj,[...arguments].slice(1)); return typeof result === 'object' ? result:obj}function Person(name,age) { this.name = name; this.age = age;}Person.prototype.sayName = function() { console.log(this.name)}Person.prototype.sayAge = function() { console.log(this.age)}const p = createNew(Person,'aa',18)p.sayName() // aap.sayAge() // 18
利用原型链的特性 让child的prototype原型指向parent的实例 这样构造原型链 child就可以访问parent的元素
缺点是 1.构造函数的属性共享, 如果是引用类型 一个子类的实例修改了这个属性 其他实例继承到的属性也会改变 2. 无法向父类传参
/**
* 原型链继承
* */
function Parent(firstName,lastName) {
this.firstName = firstName;
this.lastName = lastName;
this.friend = {
firstName:'li',
lastName:'si'
}
}
Parent.prototype.sayName = function () {
console.log(this.lastName+this.firstName)
}
function Child(firstName) {
this.firstName = firstName;
}
const parent = new Parent('san','zhang');
Child.prototype = parent
const child = new Child('si');
const child1 = new Child('si');
child.sayName() // zhangsi
parent.sayName() // zhangsan
console.log(child.friend) // { firstName: 'li', lastName: 'si' }
// 缺点是 构造函数的属性共享, 如果是引用类型 一个子类的实例修改了这个属性 其他实例继承到的属性也会改变
child1.friend.firstName = 'hua'
console.log(child1.friend) // { firstName: 'hua', lastName: 'si' }
console.log(child.friend) // { firstName: 'hua', lastName: 'si' }
可以解决原型链 引用属性共享会导致被子类修改的问题 可以向父类传递参
缺点:所有属性只能在构造函数创建 方法会被多次创建
/*构造函数继承*/
function Parent2(firstName,lastName) {
this.firstName = firstName;
this.lastName = lastName;
// 缺点是 只能在构造函数里面创建方法
this.sayName = function () {
console.log(this.lastName+this.firstName)
}
this.friend = {name:'li si'}
}
function Child2(name,age) {
Parent2.call(this,...arguments)
}
const parent2 = new Parent2('san','zhang');
const child2 = new Child2('si','zhang');
const child21 = new Child2('wu','zhang');
child2.sayName() // zhangsi
parent2.sayName() // zhangsan
console.log(child2.friend) // { name: 'li si' }
// 可以解决原型链属性共享的问题,如果是引用类型 一个子类的实例修改了这个属性 其他实例继承到的属性不会改变
child21.friend.name = 'hua hua'
console.log(child21.friend) // { name: 'hua hua' }
console.log(child2.friend) // { name: 'li si' }
利用构造函数+原型链 这样既可以在原型上定义方法,子类也可以继承,又可以让每一个子类有自己的属性 引用类型不会被子类改变 还可以向父类传参
/**
* 组合继承
* */
function Parent3(firstName,lastName) {
this.firstName = firstName;
this.lastName = lastName;
this.friend = {name:'li si'}
}
Parent3.prototype.sayName = function () {
console.log(this.lastName+this.firstName)
}
function Child3(firstName,lastName) {
Parent3.call(this,...arguments);
this.firstName = firstName;
}
Child3.prototype = new Parent3() // 缺点是调用了两次父类构造函数
const parent3 = new Parent3('san','zhang');
const child3 = new Child3('si','zhang');
const child31 = new Child3('wu','zhang');
child3.sayName() // zhangsi
parent3.sayName() // zhangsan
console.log(child3.friend) // { name: 'li si' }
child31.friend.name = 'hua hua'
console.log(child31.friend) // { name: 'hua hua' }
console.log(child3.friend) // { name: 'li si' }
利用一个父类对象 作为原型 创建其他子类对象,子类对象就会继承父类对象
缺点:无法传递参数 引用类型会被改
/**
* 原型式继承
* */
var o = {
firstName:'san',
lastName:'zhang',
sayName:function () {
console.log(this.lastName+this.firstName)
},
friend:{name:'li si'}
}
var o1 = Object.create(o) // 以o为新对象的原型创建对象
var o2 = Object.create(o)
o2.firstName='si'
o1.firstName = 'wu'
o2.friend = 'haha'
o1.sayName() // zhangwu
o2.sayName() //zhangsi
console.log(o2.friend) // haha
console.log(o1.friend) //lisi
// Object.create 自己实现 本质是内部有一个构造函数 把这个构造函数的原型指向传入的对象,返回构造函数的实例
function object(o) {
function fn() {}
fn.prototype = o;
return new fn();
}
var o11 = object(o) // 以o为新对象的原型创建对象
var o22 = object(o)
o22.firstName='si'
o11.firstName = 'wu'
o22.friend = 'haha'
o11.sayName() // zhangwu
o22.sayName() //zhangsi
console.log(o22.friend) // haha
console.log(o11.friend) //lisi
就是把 object.create 方法封装到了一个函数里面
/**
* 寄生式继承
* */
function object(obj) {
function fn {}
fn.prototype = obj
return new fn();
}
function createAnother(parent) {
var o = object(parent)
o.firstName='si'
return o
}
const oo = {firstName:'san',lastName:'zhang',friend:{name:'lisi'},sayName:function (){console.log(this.lastName+this.firstName)}}
var o3 = createAnother(oo)
var o4 = createAnother(oo)
o3.sayName() // zhangsi
o3.friend.name = 'huahua'
console.log(o4.friend) // huahua
缺点:无法传递参数 引用类型会被改
组合继承+寄生式 能解决组合继承父类构造函数会被调用两次的问题
function inherit(subType,superType) {
var prototype = Object.create(superType.prototype);
prototype.constructor = superType;
subType.prototype = prototype;
}
function SuperType(firstName,lastName) {
this.firstName = firstName;
this.lastName = lastName;
this.friend = {
name:'lisi'
}
}
SuperType.prototype.sayName= function () {
console.log(this.lastName+this.firstName)
}
function SubType(firstName,lastName) {
SuperType.call(this,...arguments)
this.age = 18;
}
inherit(SubType,SuperType)
const parent5 = new SuperType('san','zhang')
parent5.sayName() // zhangsan
const child5 = new SubType('si','zhang')
const child6 = new SubType('wu','zhang')
child5.sayName() // zhangsi
child6.sayName() // zhangwu
console.log(parent5.friend) // { name: 'lisi' }
child5.friend.name ='haha'
console.log(child5.friend) // { name: 'haha' }
console.log(child6.friend) // { name: 'lisi' }
console.log(parent5.friend) // { name: 'lisi' }
继承方式 | 描述 | 优点 | 缺点 |
---|---|---|---|
原型链继承 | 利用原型链上可以访问上一级属性,让子类的protype属性等于父类的实例 SubType.prototype=new SuperType() | 实现了继承 | 1.引用属性共享 子类可以篡改父类的属性 2. 子类无法向父类传递参数 |
构造函数继承 | 在子类的构造函数调用父类的构造函数 改变this指向子类 Super.call(this,…arguments) | 1. 应用属性不会共享 2. 子类可以向父类传递参数 | 1. 所有属性只能封装在构造函数里面 每次创建实例 方法都会被创建一次 |
组合式继承 | 原型式继承+构造函数继承 ,利用构造函数继承父类的属性,利用原型继承父类的方法 | 1. 应用属性不会共享 2. 子类可以向父类传递参数3.方法也只会创建一遍 | 父类构造函数会被调用两次 |
原型式继承 | Object.create 会以传入的对象作为原型创造一个新对象 本质是内部声明了一个构造函数 把这个构造函数的prototype属性指向这个函数 最后返回这个函数的实例 | 同原型链继承 | |
寄生式继承 | 就是把原型式继承用一个函数封装起来 其实同原型式继承 | 同上 | |
寄生组合式 | 组合式+寄生式 利用寄生式继承的特点 解决组合式继承父类构造函数会被调用两次的问题 function inhret(subType,superType){ var prototype = Object.create(superType.prototype);prototype.constructor=SuperType;subType.prototype=protptype} |
1. 应用属性不会共享 2. 子类可以向父类传递参数3.方法也只会创建一遍 4.父类构造函数也只会调用一次 |