笔记:《JavaScript面向对象精要》-第3、4章

第3章 理解对象

3.1 定义属性

当一个属性第一次被添加给对象时,JavaScript在对象上调用一个名为[[Put]]的内部方法。这个操作不仅指定了初始的值,也定义了属性的一些特征。
当一个已有的属性被赋予一个新值时,调用的是一个名为[[Set]]的方法。
//Object实例化
var person1 = new Object();
person1.name = "liang";   //调用[[Put]]的内部方法

//对象字面形式
var person2 = {
     name: "liang"
};

person2.name = "zhu";   //调用[[Set]]方法

3.2 属性探测

in 操作符在给定对象中查找一个给定名称的属性,如果找到则返回true。
in操作符会检测自有属性和原型属性,如果只要检测自有属性,使用 hasOwnProperty()方法。
var person1 = {
   name: "liang",
   age: 26
};

console.log("name" in person1);   //true
console.log(person1.hasOwnProperty("name"));   //true

//tostring是原型属性,所以用hasOwnProperty检测结果为false
console.log("toString" in person1);   //true
console.log(person1.hasOwnProperty("toString"));   //false

//可用这样一个函数来鉴别原型属性,结果为true则是原型属性
function check(object, name){
     return (name in object) && !object.hasOwnProperty(name);
}
console.log(check(person1,"age"));   //false
console.log(check(person1,"toString"));  //true

3.3 删除属性

delete 操作符针对单个对象属性调用名为[[Delete]]的内部方法。操作成功时,它返回true。
var person1 = {
   name: "liang",
   age: 26
};

delete person1.age;
"age" in person1;   //false

同名的自有属性会覆盖原型属性的值。delete 可以删除自有属性,不能删除原型属性。

3.4 属性枚举

[[Enumerable]]是属性的一个内部特征,指示属性是否可枚举,默认为true(可枚举的)。
for-in 循环会枚举一个对象所有的可枚举属性,并将属性赋给一个变量。
var person1 = {
   name: "liang",
   age: 26
};

var property;
for (property in person1){
    console.log("Name" + ": " + property);    //枚举属性
    console.log("Value" + ": " + person1[property]);    //枚举属性的值
}   
//Name: name   Value: liang   Name: age   Value: 26

Object.keys() 方法,获取可枚举属性的名字的数组。
for-in 循环会遍历原型属性,而Object.keys()只返回自有属性。
var person1 = {
   name: "liang",
   age: 26
};

//获取数组
var propertys = Object.keys(person1);
propertys;    //["name", "age"]

//输出属性和属性值
var i, len;
for(i=0, len=propertys.length; i<len; i++){
      console.log("Name" + ": " + propertys[i]);
      console.log("Value" + ": " + person1[propertys[i]]);
}     //Name: name   Value: liang   Name: age   Value: 26

3.5 属性类型

对象的属性有两种类型:数据属性和访问器属性。
数据属性只包含一个值。
访问器属性不包含值,而是定义了一个当属性被读取时调用的函数(称为getter),和一个当属性被写入时调用的函数(称为setter)。
访问器属性仅需要getter或setter两者中的任意一个,也可以两者都有。
特殊关键字get和set被用在访问器属性名字的前面,后面跟着小括号和函数体。getter被期望返回一个值,而setter则接受一个需要被赋给属性的值作为参数。
var person1 = {
     _name: "liang",    //命名规范:下划线表示该属性被认为是私有的

    get name(){
        console.log("the name is ");
        return this._name;
    },    //此处加逗号

    set name(value){
        console.log("set name to",value);
        this._name = value;
    }
};

//注意:使用的是person1.name,而不是person1._name
console.log(person1.name);   //the name is "liang"
console.log(person1._name);    //liang

person1.name = "zhu";    //set name to zhu "zhu"
console.log(person1.name);   //the name is "zhu"

console.log(Object.keys(person1));  //["_name", "name"]
通常情况下不使用访问器属性,但当你希望赋值操作会触发一些行为,或读取的值需要通过计算所需的返回值,可使用访问器属性。
如果只定义getter,该属性就变成只读。
如果只定义setter,该属性就变成只写。

3.6 属性特征

3.6.1 通用特征

有两个属性特征是数据和访问器属性都具有的。
[[Enumerable]],决定了是否可以遍历该属性。
[[Configurable]],决定了该属性是否可配置。delete可以删除可配置的属性。

Object.definedProperty() 方法,可用来改变属性特征。有3个参数:拥有该属性的对象、属性名、包含需要设置特征的属性描述对象。
var person1 = {
    name: "liang"
};

Object.defineProperty(person1,"name",{
    enumerable: false
});

console.log("name" in person1);   //true
console.log(Object.keys(person1));   // []

3.6.2 数据属性特征

数据属性额外拥有两个特征:
[[Value]] 包含属性的值。
[[Writable]] 指示该属性是否可以写入。
var person1 = {};

Object.defineProperty(person1,"name",{
    value: "liang",
    writable: true,
    enumerable: true,
    configurable: true
});
Object.defineProperty() 方法调用时,会首先检查该属性是否存在,如果不存在,将根据属性描述对象指定的特征创建。调用这个方法,如果不指定特征的布尔值,它会默认设置为false。

3.6.2 访问器属性特征

访问器属性不需要存储值,因此没有[[Value]]和[[Writable]]。
有另外两个额外特征:[[Get]]和[[Set]]
之前的例子:

var person1 = {
     _name: "liang",    //命名规范:下划线表示该属性被认为是私有的,实际上还是公有的

    get name(){
        console.log("the name is ");
        return this._name;
    },     //此处加逗号

    set name(value){
        console.log("set name to",value);
        this._name = value;
    }
};
改写成如下形式:
var person1 = {
     _name: "liang"
};

Object.defineProperty(person1,"name",{   //注意这里的name,不是_name,也可以用别的字符串来定义访问器属性
    get:function(){
        console.log("Reading name");
        return this._name;
    },
    set:function(value){
        console.log("Setting name to ",value);
        this._name=value;
    },
    enumerable:true,
    configurable:true
});
 
person1.name = "zhang";  //setting name to zhang   "zhang" 

3.6.4 定义多重属性

Object.defineProperties() 可以为一个对象同时定义多个属性。
接受两个参数:需要改变的对象、一个包含所有属性信息的对象。
var person1 = {};

Object.defineProperties(person1,{
     _name: {
        value: "liang",
        enumerable: true,
        configurable: true,
        writable: true
    },

    name: {
        get:function(){
            console.log("reading name");
            return this._name;
        },
       set: function(value){
            console.log("setting name to %s",value);
            this._name=value;
        },
       enumerable: true,
       configurable: true
    }
});

person1.name;   //reading name  "liang"
person1._name;   //"liang"

3.6.5 获取属性特征

Object.getOwnPropertyDescriptor() 方法获取属性的特征。
这个方法只适用于自有属性。接受两个参数:对象、属性名。
var person1 = {
   name: "liang"
};

var descriptor = Object.getOwnPropertyDescriptor(person,"name");
descriptor;   //{value: "liang", writable: true, enumerable: true, configurable: true}

3.7 禁止修改对象

[[Extensible]] 指明该对象是否可以被修改。有3种方法来锁定对象属性。
Object.preventExtensible() 禁止扩展,对象不能继续添加新的属性。 Object.isExtensible() 判断是否为可扩展的。
Object.seal() 封印对象,对象不能继续添加新的属性,且不能删除和改变属性类型。 Object.isSealed() 判断是否被封印的
Object.freeze() 冻结对象,对象不能继续添加新的属性,且不能删除和改变属性类型,还不能写入任何数据属性。 Object.isFrozen() 判断是否被冻结。
var person1 = {
    name: "liang"
}

Object.seal(person1);
delete person1.name;    //将无法删除
console.log(Object.isExtensible(person1));   //false
console.log(Object.isSealed(person1));   //true

第4章 构造函数和原型对象

4.1 构造函数

构造函数名的首字母要大写。
function Person(){
   //statement
}

构造函数接受一个命名参数name并将其赋给this对象的name属性。
function Person(name){
    this.name = name;
    this.sayName = function(){
        console.log(this.name);
    };
}

var person1 = new Person("liang");

person1.sayName();  //"liang"

构造函数中还能用Object.defineProperty() 方法来帮助我们初始化。
function Person(name){
    Object.defineProperty(this,"name",{
        get:function(){
            return name;
        },
        set:function(newName){
            name = newName;
       },
        enumerable:true,
        configurable:true
    });
}

4.2 原型对象

几乎所有的函数(除了一些内建函数)都有一个名为prototype的属性,该属性是一个原型对象,用来创建新的对象实例。
所有创建的对象实例共享该原型对象,且这些对象实例可以访问原型对象的属性。
当你试图访问一个对象的某个属性时,JavaScript首先在自有属性里查找该名字,如果在自有属性里没有找到则查找原型属性。

4.2.1 [[Prototype]] 属性

对象实例通过内部属性[[Prototype]]跟踪其原型对象。该属性是对象实例指向原型对象的指针。
Object.getPrototypeOf() 方法读取[[Prototype]]属性的值。
var obj = {};

console.log(Object.getPrototypeOf(obj) === Object.prototype);   // true
//任何一个泛用对象,其[[Prototype]]属性始终指向Object.prototype

isPrototypeOf() 方法检测某个对象(Object)是否是另一个对象(obj)的原型对象。
var obj = {};

console.log(Object.prototype.isPrototypeOf(obj));   //true
或者说isPrototypeOf() 是检测一个对象(person1)是否是另一个对象(Person)的对象实例。
function Person(name){
    this.name = name;
    this.sayName = function(){
        console.log(this.name);
    };
}

var person1 = new Person("liang");

Person.prototype.isPrototypeOf(person1);   //true

Object.getPrototypeOf(person1) === Person.prototype;   //true

4.2.2 在构造函数中使用原型对象

function Person(name){
    this.name = name;
}

console.log(Person.prototype);   //{constructor: ƒ}

Person.prototype.sayName = function(){
  return this.name;
};

console.log(Person.prototype);   //{sayName: ƒ, constructor: ƒ}

var person1 = new Person("liang1");

person1.sayName();    //"liang1"
上例中,sayName() 现在是一个原型属性而不是自有属性。
如下的构造函数,sayName是一个自有属性,区分这两种创建方式。
function Person(name){
    this.name = name;
    this.sayName = function(){
    return this.name;
    }
}

console.log(Person.prototype);   //{constructor: ƒ}

原型对象上存储其他类型的数据,存储引用值时要注意,这些引用值会被多个实例共享。
function Person(name){
    this.name = name;
}

Person.prototype.favorites= [];

var person1 = new Person("liang");
var person2 = new Person("zhu");

person1.favorites.push("pizza");
person2.favorites.push("quinoa");

person1.favorites;   //["pizza", "quinoa"]

通过一个对象字面形式替换原型对象,来设置多个原型属性。
function Person(name){
    this.name = name;
}

Person.prototype={
    sayName: function(){
        return this.name;
    },
    toString: function(){
        return "[Person " + this.name + "]";
    }
};

var person1 = new Person("liang");
console.log(person1.sayName());    //"liang"
console.log(person1.toString());    //"[Person liang]"
使用对象字面形式改写原型对象改变了构造函数的属性,因此它现在指向Object而不是Person。
console.log(person1.constructor === Person);    //false
console.log(person1.constructor === Object);    //true
这是因为原型对象具有一个constructor属性,这是对象实例所没有的。当一个函数被创建时,它的prototype属性也被创建,且该原型对象的constructor属性指向该函数。当使用对象字面形式改写原型对象Person.prototype时,其contructor属性将被置为泛用对象Object。
为避免这一点,手动重置constructor属性
function Person(name){
    this.name = name;
}

Person.prototype={
    constructor:Person,
    sayName: function(){
        return this.name;
    },
    toString: function(){
        return "[Person " + this.name + "]";
    }
};

构造函数、原型对象、对象实例,这三者的关系:
对于构造函数来说,prototype是作为构造函数的属性;对于对象实例来说,prototype是对象实例的原型对象。所以prototype即是属性,又是对象。
所有一切对象的原型顶端,都是Object.prototype。

4.2.3 改变原型对象

给定类型的所有对象实例共享一个原型对象。[[Prototype]]属性只是一个指向原型对象的指针,任何对原型对象的改变都立即反应到所有引用它的对象实例上。
使用Object.seal()或Object.freeze()方法,将无法添加自有属性或改变冻结对象的自有属性,但可以通过在原型对象上添加属性来扩展这些对象实例。
var person1 = new Person("liang");
var person2 = new Person("zhu");

Object.freeze(person1);

Person.prototype.sayHi = function(){
    console.log("Hi");
};

person1.sayHi();
person2.sayHi();

4.2.4 内建对象的原型对象

所有内建对象都有构造函数,因此也都有原型对象可改变。
Array.prototype.sum = function(){
    return this.reduce(function(previous,current){    //reduce()是数组方法
        return previous + current;
    });
};

var numbers = [1,2,3,4,5];
numbers.sum();   //15
在sum() 内部,this指向数组的对象实例numbers,于是该方法也可以自由使用数组的其他方法,比如reduce()。
内建对象的原型对象虽然可以改变,但不建议在生产环境中使用。可以用来做实验和验证新功能。


1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值