### 创建对象的方式:
在js中有三种方式创建对象
## 1. 字面量方式:
var obj = {key:value,key:value,...};
在字面量对象中key是对象的属性或方法名称,不能用双引号包裹;而value可以是任何类型的值
## 2. 构造函数方式:
function myFunc(){...};
var obj = new myFunc();
主要是借用了函数的原型对象的构造方法
## 3. 类方式:
class myClass{...};
var obj = new myClass();
在js中可以通过class关键字来声明类,从而使用new来创建类对象;
不过在js中对类的定性也是方法类型,可以使用typeof ClassName查看;
### 对象操作:
obj.attrName obj['attrName'] // 调用属性
obj.funcName() // 调用方法
obj.newAttrName = attrValue // 设置新属性
obj.attrName = newAttrValue // 修改属性
obj.newFuncName = function(){...} // 设置新方法
delete obj.attrName // 删除对象
### 对象原型
## 简要
创建对象并不是复制继承的属性和方法,而是创建对象的原型指向所继承的构造函数原型对象
## 查看对象原型
obj.__proto__; // 方式1
Object.getPrototypeOf(obj); // 方式2
### 构造函数原型对象
## 简要:
方法和类(特殊方法)都可以称为构造函数,因此他们都有原型对象
## 查看构造函数原型对象:
myFunc.prototype
### 对象原型和构造函数原型对象
根据对象原型的概念,毫无疑问的可以得到结论:对象原型等于构造函数原型对象。
### 原型探索
## 1. 定义一个构造函数
function f1(){};
【函数.prototype={} >> 添加constructor方法到prototype >> prototype.__proto__ == Object() >> prototype.__proto__.__proto__ == Object().__proto__ == null】
## 2. 用构造函数创建一个对象
var f2 = new f1();
【f2.__proto__ = f1.prototype = f2.__proto__.constructor.prototype】
## 3. 使用构造函数的原型对象定义属性和调用
f1.prototype.attr1 = 'value1';
f2.attr1;
【f1.prototype.attr1 = 'value1'】
【f2.attr1 >> f2.__proto__.attr1 >> f1.prototype.attr1】
## 4. 使用构造函数的原型对象定义方法和调用
f1.prototype.func1 = function(){...};
f2.func1();
【f1.prototype.func1 = function(){...}】
【f2.func1() >> f2.__proto__.func1() >> f1.prototype.func1()】## 5. 根据对象来创建新的对象(伪继承)
var f3 = Object.create(f2);
【f3.__proto__ == f2】
【f3.__proto__.__proto__ == f2.prototype == f1.prototype】
## 6. 对象的构造函数
f2.constructor;
f3.constructor;
f2.constructor.name;
【f2.constructor == f3.constructor】### 构造函数原型面向对象编程
通过构造函数原型,我们可以定义属性和方法,然后创建对象也可以调用继承自构造函数的属性和方法,下面将学习如何正确地使用构造函数原型来面向对象编程;
## 对象属性
【虽然可以使用construct_func.prototype.attr = value,但是无法将属性绑定给实例对象,即使使用construct_func.prototype.attr = this.value,全局使用this是不能绑定到构造函数原型对象的。所以应该将属性定义在构造函数体内,这样实例化对象时就可以为对象绑定属性值】
function construct_func(attr1,attr2){
this.attr1 = attr1;
this.attr2 = attr2;
}
var obj = new construct_func('joden',18);
obj.attr1;
obj.attr2;
## 对象方法
【与对象属性不同,使用construct_func.prototype.func = function(){...}来定义方法,可以绑定方法到构造函数的原型对象中,且在方法体中调用this指向的是调用其的对象,也就是方法所在的对象即构造函数原型对象,所以在构造函数原型中定义方法是可行的;而如果在构造函数内定义函数,实例化时也不会为对象绑定函数】
construct_func.prototype.func1 = function(){...};
construct_func.prototype.func2 = function(){...};
obj.func1();
obj.func2();
## 结论:
构造函数内定义属性,构造函数原型对象定义方法。### 总结:
(1) 对象的原型指向其构造函数原型对象
(2) new关键字创建一个实例对象并不是复制了构造函数的的属性和方法,而是将该实例对象的原型指向了其构造函数的原型对象;(3) 对象 >> 原型 >> 对象 >> 原型 >> 对象 >> ... (原型链)
(4) 可以使用Object.create(obj)来创建一个新对象,且新对象的__proto__(原型)指向原对象;
(5) 可以使用obj.constructor[.name]来查看对象的构造函数[名称](6) 借助构造函数的原型对象来面向对象编程;构造函数内定义属性,构造函数原型对象定义方法。
### 前言:
把普通函数作为构造函数虽然也可以实现面向对象,但要实现面对对象深层继承特性还是不好实现;所以为此js中类的提出,可以让我们更好地完成面向对象的所有特性(继承,封装等等);
### 类的定义:
class Person{
// 属性
name;
// 构造方法
constructor(name){
this.name = name;
}
// 方法
myFunc(){
...
}
}
### 实例对象和属性调用:
var p1 = new Person('joden');
p1.name;
p1.myFunc();### 类继承:
class Student extends Person{
id;
constructor(id,name){
super(name);
this.id = id;
}
learn(){
...
}
}
## super()
在子类中使用super(*args)来调用父类的构造方法或方法### 私有属性和方法(封装):
class Test{
// 私有属性year
#year;
// 构造方法
constructor(year){
this.#year = year;
}
// 私有方法
#myFunc(){
...
}
}### 总结:
(1) 方法与函数不同,方法定义在类中,且不用function关键字声明;
(2) 类的构造方法名为constructor,且构造方法不是必须的;
(3) 私有属性和方法,通过在属性名或方法名前加“#”号来显示表示;
(4) 类中用this指向当前实例对象;