Object.assign()
该函数接收两至多个参数:第一个参数为目标对象,之后的参数作为源对象,将源对象中可枚举和自有属性复制到目标对象;
- 对每个源对象执行的是浅复制
const increment = {
count:{
value: 1
},
sum(){
this.count.value++
}
}
let obj2 = {
count:3
}
obj2 = Object.assign(obj2,increment)
increment.sum()
obj2.sum()
console.log('1:',increment.count)//1:3
console.log('2:',obj2.count)//2:3
//increment与obj2两个对象中的count执行的是浅复制:只复制对象的引用
对象解构
- 由于对象的属性间没有先后顺序,因此必须注明解构模式:
- 解构模式在左,表示匹配哪个,变量命名在右;想要解构的变量的结构与对象中是一致的;
const target = {
value: 1,
fruit: {
apple: 0,
banana: 1,
orange: 2
},
vegetable: {
tomato: {
like:'yes',
number: 1
},
potato: {
like:'no',
number:0
}
}
}
//解构target,获得value
const { value:value_new } = target
console.log(value_new)//1
//获得fruit与apple
const {fruit:fruit_new,fruit:{ apple:apple_new }} = target
console.log(fruit_new)//对象
console.log(apple_new)//0
//获得vegetable与potato与likes
const {
vegetable:vegetable_new,
vegetable:{
potato:potato_new
},
vegetable:{
potato:{
like:like_new
}
}
} = target
console.log(vegetable_new)
console.log(potato_new)
console.log(like_new)
- 如果一个解构表达式涉及多个赋值,开始的解构赋值成功,后面出错,则整个解构只会完成一部分;匹配不到的值为undefined
- 在函数参数列表中也可以进行解构赋值,对参数的解构赋值不会影响arguments对象,但可以在函数签名中声明在函数体内使用局部变量
8.2 创建对象
8.2.3构造函数模式
构造函数名称的首字母都是要大写的,非构造函数则以小写字母开头。
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = function(){
console.log(this.name)
}
}
8.2.4原型模式
- 每个函数都会创建一个prototype属性,这个属性是一个对象,包含应该由特定引用类型的实例共享的属性和方法。实际上,这个对象就是通过调用构造函数创建的对象的原型。
- 使用原型对象的好处是,在它上面定义的属性和方法可以被对象实例共享。
1.理解原型
无论何时,只要创建一个函数,就会按照特定的规则为这个函数创建一个prototype属性(即原型对象)。默认情况下,所有原型对象自动获得一个名为constructor的属性,指回与之关联的构造函数。
每次调用构造函数创建一个新实例,**这个实例的内部[[Prototype]]指针就会被赋值为构造函数的原型对象**。一些浏览器(Firefox,Safari,Chrome)会在每个对象上暴露__proto__属性,通过这个属性可以访问对象的原型。
构造函数的原型也是一个对象,因此其内部也有[[Prototype]]属性
2.练习题
//练习1
var F = function() {};
Object.prototype.a = function() {
console.log('a');
};
Function.prototype.b = function() {
console.log('b');
}
var f = new F();
f.a();//a
//f没有a属性,因此查找f的__proto__属性(F.prototype)上也没有a,
//因此查找F.prototype.__proto__上(Object.prototype)
f.b();//报错
//f没有b属性,因此查找f的__proto__属性(F.prototype)上也没有b,
//因此查找F.prototype.__proto__上(Object.prototype)上也没有b
//Object.prototype.__proto__为null:undefined
F.a();//a
//F中没有
//F.__proto__(Function.prototype)中没有a方法
//F.__proto__.__proto__(Function.prototype.__proto__)为Object.prototype,a方法是a
F.b();//b
//F中没有
//F.__proto__(Function.prototype)中b方法
//练习2
var A = function() {};//A函数,具有prototype属性
A.prototype.n = 1;
var b = new A();//b是实例对象,b.__proto__指向A.prototype,是一个引用对象
//这里将A.prototype重新赋值,指向了一个新的对象
//但原来的对象还保持着b对他的引用,所以垃圾回收机制不会清除原来这个对象,b中的地址不会改成新的
A.prototype = {
n: 2,
m: 3
}
var c = new A();
console.log(b.n);//1
console.log(b.m);//undefined
console.log(c.n);//2
console.log(c.m);//3
var foo = {},
F = function(){};
Object.prototype.a = 'value a';
Function.prototype.b = 'value b';
console.log(foo.a);//value a
//foo没有a属性
//foo.__proto__(Object.prototype)中a属性为:value a
console.log(foo.b);//undefined
//foo没有b属性
//foo.__proto__(Object.prototype)中没有b属性
//Object.prototype.__proto__为null
console.log(F.a);//value a
//F没有a属性
//F.__proto__(Function.prototype)没有a属性
//F.__proto__.__proto__(Function.prototype.__proto__)为Object.prototype
console.log(F.b);
//F没有b属性
//F.__proto__(Function.prototype)b属性为value b
//练习4
function A() {}
function B(a) {
this.a = a;
}
function C(a) {
if (a) {
this.a = a;
}
}
A.prototype.a = 1;
B.prototype.a = 1;
C.prototype.a = 1;
console.log(new A().a); //1
//new A()没有a属性,该实例对象的__proto__即A.prototype.a = 1
console.log(new B().a);//undefined
console.log(new C(2).a);//直接找new C(2)对象中的a,是2
//练习5
console.log(123['toString'].length + 123)//126
//练习6
function Fn() {
this.x = 100;
this.y = 200;
this.getX = function () {
console.log(this.x);
}
}
Fn.prototype.getX = function () {
console.log(this.x);
};
Fn.prototype.getY = function () {
console.log(this.y);
};
let f1 = new Fn;
let f2 = new Fn;
console.log(f1.getX === f2.getX);//这里getX找到的是每个实例对象身上的,false
console.log(f1.getY === f2.getY);//getY是原型对象身上的因此是共享的,true
console.log(f1.__proto__.getY === Fn.prototype.getY);//true
console.log(f1.__proto__.getX === f2.getX);//false
console.log(f1.getX === Fn.prototype.getX);//false
console.log(f1.constructor);//f1没有constructor属性,因此从f1.__proto__查找(Fn.prototype)上的constructor属性为Fn函数
console.log(Fn.prototype.__proto__.constructor);//(Object.prototype.constructor属性为Object函数)
f1.getX();//100
f1.__proto__.getX();//this指向原型对象,undefined
f2.getY();//
Fn.prototype.getY();//undefined
//练习7
function fun(){
this.a=0;
this.b=function(){
alert(this.a);
}
}
fun.prototype={
b:function(){
this.a=20;
alert(this.a);
},
c:function(){
this.a=30;
alert(this.a)
}
}
var my_fun=new fun();
my_fun.b();//"0"
my_fun.c();//"30"
记得 函数 和 对象 分别有隐式原型和显式原型就行了,原型链是顺着隐式原型找的,而隐式原型是指向其构造函数的显式原型的!!!
- delete 操作符可以删除实例上的属性,从而让标识符解析过程能够继续搜索到原型对象;
3.关于原型的一些函数
- 实例对象如何获取隐式原型对象,即__proto__:一些浏览器可以实现通过obj.__proto__获取,但也可以通过函数Object.getPropertyOf(),返回参数的内部特性[[prototype]]的值;
- Object.create()创建一个新对象,同时为其指定原型:
let tmp = {
numLegs:2
}
let person = Object.create(tmp)
person.name = 'Matt'
console.log(person.name)//Matt
console.log(person.numLegs)//2
console.log(Object.getPropertyOf(person) === tmp)//true
- hasOwnProperty()方法用于确定某个属性在实例上还是在原型对象上;当属性存在于实例对象上返回true:
function Person(){}
Person.prototype = {
name:'Nicholas',
job:'engineer'
}
let person1 = new Person()
let person2 = new Person()
console.log(person1.name)//Nicholas
console.log(person1.hasOwnProperty("name"))//false
person1.name = 'Greg'
console.log(person1.hasOwnProperty("name"))//true
4.Object.getOwnPropertyDescriptor()方法可以将对象的属性描述出来:
function Person(){}
Person.prototype = {
name:'Nicholas',
job:'engineer'
}
let per1 = new Person()
console.log(Object.getOwnPropertyDescriptors(per1))
//{}
per1.name = 'Greg'
console.log(Object.getOwnPropertyDescriptors(per1))
//{
// name:{
// value:'Greg',
// writable: true,
// enumerable: true,
// configurable: true
// }
//}
- 原型和in操作符: 两种使用in的方式:单独使用in操作符和在for-in循环中使用;在单独使用时,in操作符会在可以通过对象访问指定属性时返回true,无论该属性是在实例上还是原型上。 因此可以使用hasOwnProperty()方法和in方法可以判断某个属性是否在原型对象上,当in为true,hasOwnProperty()为false,则说明该属性在原型对象上。