JS红宝书复习章八:对象,类与面向对象编程

169 阅读5分钟

Object.assign()

该函数接收两至多个参数:第一个参数为目标对象,之后的参数作为源对象,将源对象中可枚举和自有属性复制到目标对象;

  1. 对每个源对象执行的是浅复制
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.关于原型的一些函数

  1. 实例对象如何获取隐式原型对象,即__proto__:一些浏览器可以实现通过obj.__proto__获取,但也可以通过函数Object.getPropertyOf(),返回参数的内部特性[[prototype]]的值;
  2. 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
  1. 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
//    }
//}
  1. 原型和in操作符: 两种使用in的方式:单独使用in操作符和在for-in循环中使用;在单独使用时,in操作符会在可以通过对象访问指定属性时返回true,无论该属性是在实例上还是原型上。 因此可以使用hasOwnProperty()方法和in方法可以判断某个属性是否在原型对象上,当in为true,hasOwnProperty()为false,则说明该属性在原型对象上。