对象 原型 原型链
对象
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/fromEntries
对象的方法
1 Object.assign(target,source1,source2) 对象合并,合并到target对象上
2 Object.create(proto,null) 已现有对象为原型,创建一个新对象
3 Object.defineProperties(obj,{property1:{},property2:{},…}) 在对象上新增新属性或修改现有属性,返回此对象
属性配置的描述
configurable 该属性是否可以被删除且是否修改配置项 默认false
enumerable 该属性是否可被枚举 默认false
value 该属性值
writable 该属性是否可以重新被复制 默认false
get 该属性的getter函数,函数返回值用作属性的值
set 该属性的setter函数,只有一个参数,属性的新值
4 Object.definedProperty(obj,property,{}) 在对象上定义一个新属性或者修改现有属性,返回此对象
5 Object.entries() 返回一个包含[key,value]对象所有的属性名和属性值 [[key,value],[key,value]]
6 Object.fromEntries() 将键值对列表转化为对象 如:map对象
const object1 = { a: 1, b: 2, c: 3 };
const object2 = Object.fromEntries(
Object.entries(object1).map(([key, val]) => [key, val * 2]),
);
console.log(object2);
// { a: 2, b: 4, c: 6 }
原型 原型链
原型的理解
原型链的理解
对象的封装
生成实例对象的原始模式
通过字面量生成
let cat1 = {};
cat1.name = 'zs';
cat1.color='黑色';
let cat2 = {};
cat1.name = 'ls';
cat1.color='白色';
缺点:1 生成多个实例,写起来麻烦;2 实例与原型之间,没有任何办法看出联系
原始模式改进
写一个函数,解决代码重复问题
function Cat(name,color){
return {
name:name,
color:color
}
}
let cat1 = Cat('zs',黑色)
let cat2 = Cat('ls',白色)
缺点: 应然看不出cat1与cat2 之间的联系,不能反映他们是同一个原型对象的实例
构造函数模式
// 原型对象
function Cat(name,color){
this.name = name;
this.color = color;
}
// 生成实例对象
let cat1 = new Cat('zs',黑色)
let cat2 = new Cat('ls',白色)
cat1和cat2会自动含有一个constructor属性,指向他们的构造函数
cat1.constructor = Cat // true
cat2.constructor = Cat // true
instanceof验证原型对象和实例之间的关系
cat1 instanceof Cat // true
cat2 instanceof Cat // true
构造函数的问题
浪费内存
function Cat(name,color){
this.name = name;
this.color = color;
this.type = '猫科动物';
this.eat = function(){alert('吃老鼠')}
}
let cat1 = new Cat('zs',黑色)
let cat2 = new Cat('ls',白色)
cat1.type // 猫科动物
cat1.eat() // 吃老鼠
缺点: 每一个实例对象,type属性和eat()方法都是一模一样的内容,每一次生成一个实例,都必须为重复的内容,导致多占内存
cat1.eat === cat2.eat //false 指向不同的内存地址
prototype模式
每一个构造函数都有一个prototype属性,指向另一个对象。这个对象的所有属性和方法都会被构造函数的实例继承
function Cat(name,color){
this.name = name;
this.color = color;
}
Cat.prototype.type = '猫科动物';
Cat.prototype.eat = function(){
alert('吃老鼠')
}
let cat1 = new Cat('zs',黑色)
let cat2 = new Cat('ls',白色)
cat1.type // 猫科动物
cat1.eat() // 吃老鼠
cat1.eat === cat1.eat // true
对象的继承
猫来继承动物
function Animal(){
this.species = '动物'
}
function Cat(name,color){
this.name = name;
this.color = color;
}
构造函数绑定
使用call或apply方法,把父对象的构造函数绑定到子对象上
function Cat(name,color){
Animal.apply(this,arguments);
this.name = name;
this.color = color;
}
let cat1 = new Cat('zs','黑色')
cat1.species // 动物
prototype模式
Cat.prototype = new Animal();
<!-- 每个prototype对象都有一个constructor属性,指向他的构造函数,所以需要让Cat.prototype.constructor重新指向他自己的构造函数 -->
Cat.prototype.constructor = Cat;
直接继承prototype
第三种方法是第二种方法的改进,由于Animal对象中,不变的属性都可以直接写到 Animal.prototype中,可以直接让Cat()跳过Animal(),直接继承Animal.prototype
重写Animal构造函数
function Animal(){};
Animal.prototype.species = '动物';
继承
Cat.prototype = Animal.prototype;
Cat.prototype.constructor = Cat;
优点:效率比较高,不用执行和建立Animal实例,省内存
缺点:Cat.prototype和Animal.prototype指向同一个对象,则对Cat.prototype的修改会有反应到Animal.prototype
Animal.prototype.constructor === Cat // true
利用空对象作为中介
由于“直接继承”有上述缺点,则利用一个空对象作为中介
let F = function(){};
F.prototype = Animal.prototype;
Cat.prototype = new F();
Cat.prototype.constructor = Cat;
F是空对象,所及几乎不占用内存
new Fun() 发生了什么?
1 在内存中创建一个新对象
2 让this指向这个空对象
3 执行构造函数中的代码,给这个新对象添加属性和方法
4 返回这个新对象