js高级程序设计笔记 -- 属性类型以及创建对象

1.属性类型

ECMAScript中有两种属性:数据属性和访问器属性

1.数据属性:

数据属性包含一个数据值的位置,在这个位置可以读取和写入值
[[ Configurable ]] : 表示能否删除属性或者修改属性为访问器属性。默认值为true;
[[ Enumerable ]] : 表示能否通过for-in 循环返回属性。默认值为true;
[[ Writable ]] : 表示能否修改属性的值 。默认值为true;
[[ Value ]] : 包含这个属性的数据值,读取属性值的时候从这个位置读取,写入属性值的时候,把新值保存在这个位置 默认值为 undefined;

修改数据属性默认的特性,必须使用 ECMAScript5 的object.defineProperty() 方法。这个方法接受三个参数:
属性所在的对象,属性的名字和一个描述符对象,其中描述符对象的属性必须是: configurable, enumerable, writable, value.
var person = {};
Object.defineProperty(person, "name", {
  writable: false,
  value: "mnixu"
})

console.log(person.name) //"mnixu"
person.name = "xu"
console.log(person.name) //"mnixu"

var person = {};
Object.defineProperty(person, "name", {
  configurable: false,
  value: "mnixu"
})
console.log(person.name) //"mnixu"
delete person.value;
console.log(person.name) //"mnixu"

var person = {};
Object.defineProperty(person, "name", {
  configurable: false,
  value: "mnixu"
})
//拋出錯誤
Object.defineProperty(person, "name", {
  configurable: true,
  value: "mnixu"
})
注意:一但把属性定义为不可配置的,就不能再把它变为可配置的。
在调用Object.defineProperty()方法创建一个新的属性时,如果不指定,configurable, enumerable, writable,特性的默认值都为false,
如果调用Object.defineProperty()方法只是修改已定义的属性,则无限制
 

2.访问器属性:

访问器属性不包含数据值:他们包含一对getter和setter函数。
也有四个特性。
[[ Configurable ]] : 表示能否删除属性或者修改属性为访问器属性。默认值为true;
[[ Enumerable ]] : 表示能否通过for-in 循环返回属性。默认值为true;
[[ get ]] : 在读取属性时调用的函数。默认值为undefined;
[[ set ]] : 在写入属性是调用的函数。 默认值为undefined;

访问器属性不能直接定义,必须 使用object.defineProperty() 方法 来定义

var book = {
  _year: 2004,
  edition: 1
}
Object.defineProperty(book, "year", {
  get: function () {
    return this._year;
  },
  set: function (newValue) {
    if (newValue > 2004) {
      this._year = newValue;
      this.edition += newValue - 2004
    }
  }
})

book.year = 2005;
console.log(book.edition)

_year前面的下划线是用于表示只能通过对象方法访问的属性。
getter函数返回_year的值,setter函数通过计算来确定正确的版本,修改year属性为2005时候会导致_year变为2005
edition变为2,这是使用访问器属性的常见方式,即设置一个属性的值会导致其他属性发生变化。

2.定义多个属性

 使用Object.defineProperties()方法可以定义多个属性

var book = {};
Object.defineProperties(book, {
  _year: {
    writable: true,
    value: 2004
  },
  edition: {
    writable: true,
    value: 1
  },
  year: {
    get: function () {
      return this._year;
    },
    set: function (newValue) {
      if (newValue > 2004) {
        this._year = newValue;
        this.edition += newValue - 2004
      }
    }
  }
}); 


这个定义的最终对象和上面定义的对象相同,唯一的区别是这里的属性都是在同一时间创建的。

3.读取属性的特性

使用Object.getOwnPropertyDescriptor() 可以取得给定义属性的描述符
例子:
var descriptor = Object.getOwnPropertyDescriptor(book, "_year")
console.log(descriptor.value) //2004

var descriptor = Object.getOwnPropertyDescriptor(book, "year")
console.log(descriptor.enumerable)  //false 
console.log(typeof descriptor.get)  //"function"

2.创建对象

1.构造函数模式

function Person(name, age, job) {
  this.name = name;
  this.age = age;
  this.job = job;
  this.sayName = function() {
    console.log(this.name)
  }; 
}

var person1 = new Person("mnixu", "20", "Student");
var person2 = new Person("zhangsan", "11", "Student");
//person1 和 person2 都保存着Person 的不同实例。都有一个constructor(构造函数)属性,该属性指向Person
console.log(person1.constructor == Person); //true
console.log(person2.constructor == Person); //true

//对象的constructor属性最初使用来标识对象类型的。但是,检测对象类型使用instanceof操作符更可靠
console.log(person1 instanceof Person); //true
console.log(person1 instanceof Object); //true
console.log(person2 instanceof Person); //true
console.log(person2 instanceof Object); //true

构造函数也可以当做普通函数来调用:
//当做构造函数使用
var person = new Person("mnixu", "20", "Student");
person.sayName(); //"mnixu"

//当做普通函数使用
Person("zhangsan", "20", "Student"); //添加到window
window.sayName(); //"zhangsan"

//在另一个对象的作用域中调用
var o = new Object();
Person.call(o, "lisi", "20", "Student");
o.sayName(); //"lisi"

构造函数模式虽然好用,但是使用构造函数有一个主要问题,就是每个方法都要在每个实例上重新创建一遍。
前面的person1 和 person2 都有一个sayName()方法,但是两个方法不是同一个Function实例。
在ECMAScript中的函数就是对象,每定义一个函数,就是实例化一个对象。
console.log(person1.sayName == person1.sayName); //false

2.原型模式

我们创建的每一个函数都有一个prototype(原型)属性。这个属性是一个指针,指向一个对象。
使用原型对象可以让所有对象实例共享它所包含的属性和方法。
function Person() { };

Person.prototype.name = "mnixu";
Person.prototype.age = "20";
Person.prototype.job = "Student";
Person.prototype.sayName = function () {
  console.log(this.name);
}; 
 
var person1 = new Person();
person1.sayName(); //"mnixu"
var person2 = new Person();
person2.sayName(); //"mnixu"

console.log(person1.sayName == person1.sayName); //true
创建了自定义的构造函数之后,其原型对象默认只会取得constructor属性;至于其他方法,都是从Object继承过来的。
当我们调用构造函数创建一个新的实例后,该实例内部讲包含一个指针(内部属性),指向构造函数的原型对象。
ES5管这个指针叫[[ Prototype ]] 。我们可以通过__proto__这个内置属性来访问
console.log(person1.__proto__ === Person.prototype); //true
console.log(Person.prototype.__proto__ === Object.prototype); //true
console.log(Object.prototype.__proto__) //null
当我们访问对象的某个属性时,会从对象实例本身开始寻找,没找到就继续搜索指针指向的原型对象,直至找到顶层null为止
我们把上面的例子串起来就是原型链
person1 --> Person.prototype --> Object.prototype --> null
 原型对象prototype中都有个预定义的constructor属性,用来引用它的函数对象。这是一种循环引用
 Person.prototype.constructor === Person //true
 Function.prototype.constructor === Function //true
 Object.prototype.constructor === Object //true

原型模式也是有缺点的,但是原型中的所有属性是被很多实例共享,对于包含引用类型值的属性来说,可能会有很多问题
例子:
function Person() { };

Person.prototype = {
  constructor: Person,
  name: "mnixu",
  age: 20,
  job: "Student",
  friends: ["zhangsan", "lisi"],
  sayName: function () {
    console.log(this.name)
  }
}

var person1 = new Person();
var person2 = new Person();

person1.friends.push("wangwu");
console.log(person1.friends); //["zhangsan", "lisi", "wangwu"]
console.log(person2.friends); //["zhangsan", "lisi", "wangwu"]

console.log(person1.friends === person2.friends) //true

3.组合使用构造函数模式和原型模式

创建自定义类型的常见方式就是组合使用构造函数模式和原型模式,构造函数模式用于定义实例属性,原型模式用于定义方法和共享的属性。
所以,每个实例都有自己的一份实例属性副本,但同时又共享着对方法的引用。
function Person(name, age, job) {
  this.name = name;
  this.age = age;
  this.job = job;
  this.friends = ["zhangsan", "lisi"];
}

Person.prototype = {
  constructor: Person,
  sayName: function () {
    console.log(this.name)
  }
}

var person1 = new Person("mnixu", 20, "Student");
var person2 = new Person("lisi", 20, "Student");

person1.friends.push("wangwu")

console.log(person1.friends); //["zhangsan", "lisi", "wangwu"]
console.log(person2.friends); //["zhangsan", "lisi"] 
console.log(person1.friends === person2.friends)  //false
console.log(person1.sayName === person2.sayName)  //true 









  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值