1、对象
对象是一组属性的无序集合,通常使用对象字面量来创建对象:
let person = {
name: "Nicholas",
age: 29,
job: "Software Engineer",
sayName() {
console.log(this.name);
}
}
对象属性的类型
这里自己的理解数据属性和访问器属性的设计,为我们提供了使用js对象的灵活性,我们可以有两种方式定义对象的属性,当我们直接使用对象字面量的方式创建对象,对象的属性是数据属性,也可以定义访问器属性来增强js对象。
属性分为两种:数据属性和访问器属性。
数据属性:数据属性包含一个保存数据值的位置。数据属性有以下几个内部特性:
<font style="color:rgb(0,0,0);">[[Configurable]]</font>
<font style="color:rgb(0,0,0);">[[Enumerable]]</font>
<font style="color:rgb(0,0,0);">[[Writable]]</font>
<font style="color:rgb(0,0,0);">[[Value]]</font>
访问器属性
访问器属性不包含数据值,而是包含一个获取(get)函数和设置(set)函数。访问器属性有以下几个内部特性
<font style="color:rgb(0,0,0);">[[Configurable]]</font>
<font style="color:rgb(0,0,0);">[[Enumerable]]</font>
<font style="color:rgb(0,0,0);">[[Get]]</font>
<font style="color:rgb(0,0,0);">[[Set]]</font>
访问器属性通常用于这种情形:当我们有一些属性不希望直接被外部访问,或设置对象的一个属性值会导致一些其他的变化。
// 定义一个对象,包含伪私有成员year_和公共成员edition
let book = {
year_: 2017,
edition: 1
};
Object.defineProperty(book, "year", {
get() {
return this.year_;
},
set(newValue) {
if (newValue > 2017) {
this.year_ = newValue;
this.edition += newValue - 2017;
}
}
});
book.year = 2018;
console.log(book.edition); // 2
不管是数据属性还是访问器属性,我们都需要使用Object.defineProperty
方法。
下边这张图,当我们给name属性定义访问器属性后,person
对象的name属性就变为了一个访问器属性,age还是一个数据属性。
****
2、创建对象
ES6之前js是没有class关键字的(ES6也只是原型的语法糖),当我们创建某种类型的对象时,通常是创建自定义构造函数,以函数的形式为自己的对象类型定义属性和方法。(其实是利用js 函数的特性的一种取巧的办法)
构造函数本身也是函数,js中没有特定定义构造函数的语法,任何函数使用new 操作符就是构造函数。
function Person() {
this.name = "Jake";
this.sayName = function() {
console.log(this.name);
};
}
// 作为构造函数
let person = new Person("Nicholas", 29, "Software Engineer");
person.sayName(); // "Nicholas"
// 作为函数调用
Person("Greg", 27, "Doctor"); // 添加到window对象
window.sayName(); // "Greg"
// 在另一个对象的作用域中调用
let o = new Object();
Person.call(o, "Kristen", 25, "Nurse");
o.sayName(); // "Kristen"
3、原型
原型是js实现类、继承等最重要的因素。
3.1 基础
prototype
每个函数都会创建一个prototype
属性,这个属性是一个对象,包含应该由特定引用类型的实例共享的属性和方法。实际上,这个对象就是通过调用构造函数创建的对象的原型。
原型对象默认会有constructor
属性,指向与之关联的构造函数。
下边这张图说明了<font style="color:rgb(0,0,0);">Person</font>
<font style="color:rgb(0,0,0);">Person</font>
<font style="color:rgb(0,0,0);">Person</font>
<font style="color:rgb(0,0,0);">Person</font>
<font style="color:rgb(0,0,0);">Person.prototype</font>
<font style="color:rgb(0,0,0);">Person.prototype</font>
<font style="color:rgb(0,0,0);">Person.prototype.contructor</font>
<font style="color:rgb(0,0,0);">Person</font>
<font style="color:rgb(0,0,0);">Person</font>
<font style="color:rgb(0,0,0);">Person.prototype</font>
**<font style="color:rgb(0,0,0);">Person</font>**
proto
既然有了prototype,那__proto__又是什么呢,为什么会出现?
__proto__
属性之前不是js标准,但由于各大浏览器厂商都已经实现了,才被加入了ES6。
在JS中,任何对象都有__proto__
属性,__proto__
可被称为隐式原型(区别于prototype
),指向构造该对象的构造函数的原型。
也就是实例的__proto__属性
指向实例构造函数的原型。
我们在浏览器调试时,经常会看到层层嵌套的__proto__属性,这是因为比如Person的实例person1,
person1
有__proto__
属性,而
person1.__proto__
也是一个对象,也有__proto__对象
,这样层层嵌套,直到__proto__
属性是一个object类型
我们通过下边这张图来看一下:
Person
函数有prototype
原型属性,同时Person
函数也是一个funtion
类型的对象,有__proto__
隐式原型属性。Person
是一个函数,也是一个**function**
类型的对象,__proto__
隐式原型属性指向的function
的构造函数。person1
实例是一个对象,只有__proto__
隐式原型属性。指向Person prototype
。
prototype 与 __proto__的一些总结
- 对象具有
__proto__
、prototype
,函数只有prototype
- 对象的
__proto__
属性指向-->构造该对象的构造函数的原型
3.2 继承
原型可以解决对象之间共享属性和方法的问题,也就是继承,但直接使用原型实现继承的话,会带来一个问题:对于方法的继承来说是可以的,但对于属性为引用值来说,多个实例之间共享同一个引用值属性,一个实例修改引用值属性会造成其他实例也会受到影响。
因为上述的一些问题,开发者们想出了很多办法,利用原型的特性实现继承:
- 开发者们想出来通过****
4、类
es6引入了class关键字,但本质上只是语法糖,仍是对原型的封装,
class Person {
constructor(name) {
this.name = name
},
getName(){
return this.name
}
}
当我们
class extend