关于js的类的一些小见解
这是我学习js的一点点小的体会和心得,可能会有一些不对的地方欢迎提出,谢谢
创建对象
- js是一中弱类型的语言,他的类的创建时多种多样的,在ES5中,没有正式支 持面向对象的结构,比如类和继承。而在ES6虽然支持了类和继承,但是仅仅封装了ES5.1的构造函数加原型链继承的语法糖
- 首先我们来看一下ES5类的创建
工厂函数模式 ,
工厂函数时通过函数的形式来创建一个对象,他的构造函数是 Object 而这个createObject只是封装了创建对象的过程,添加属性和方法通过 “.”来添加。工厂函数模式效率过低,函数无法重用。更为致命的是没有解决对象表示的问题,即所有工厂函数创建出来的类型都是Object
function createObj(name, age, sex) {
let o = new Object();
o.name = name;
o.age = age;
o.sex = sex;
o.say = function() {
console.log(`name:${this.name} age:${this.age} sex:${this.sex}`)
}
return o
}
let a = createObj('张三', 12, '男')
a.say() //name:张三 age:12 sex:男
console.log(a.constructor === createObj) //false
console.log(a.constructor === Object) //true
console.log(a.constructor) //Object() { [native code] }
构造函数模式
ES中的构造函数以函数的形式创建特定的对象,用于创建特定类型的对象。比如Object和Array就是原生的构造函数。运行时可以直接在环境中使用
```javascript
function CreateObjA(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
this.say = function() {
console.log(`name:${this.name} age:${this.age} sex:${this.sex}`)
}
}
function CreateObjB(name, age, sex) {
this.name = name;
this.age = age;
this.say = function() {
console.log(`name:${this.name} age:${this.age} `)
}
}
let a = new createObjA('张三', 12, '男')
let b = new createObjB('张三', 12, '男')
a.say() //name:张三 age:12 sex:男
console.log(a.constructor === createObjA) //true
console.log(a.constructor === Object) //false
console.log(a.constructor === b.constructor) //false
需要注意的是构造函数也是函数,他可以直接调用,调用后他的方法可以通过window访问到,但是属性不能
console.log(window.name) //undefined
window.say() //name:undefined age:undefined sex:undefined
需要注意的是构造函数他存在一定性能问题,他定义的方法在每一个实例中都会创建一遍,存在一定的内存浪费。
这个时候需要使用到原型模式
原型模式
每个函数都有一个prototype属性,这个属性是一个对象。包含因该由特定的实例共享的属性和方法,在他上面定义的所有属性和方法可以被每一个实例共享
理解原型
每一个函数的创建,都会按照特定的规则为合格函数创建一个prototype。默认情况下每个函数都会获得一个constructor属性,这个属性默认指向与他关联的构造函数。在这里我通过原型函数创建了一个对象,代码下的图展示了这个关系(字写的有点丑,见谅)
function Person() {} //按照规则,够着函数首字母一般大写
Person.prototype.name = '张三'
Person.prototype.age = 24
Person.prototype.say = function() {
console.log(`我叫${this.name},今年${this.age}了`)
}
let person1 = new Person()
person1.say() //我叫张三,今年24了
person1.name = '李四'
//在这个实例中,person1通过点在函数内创建了一个私有的name属性,覆盖了原型链中的name
person1.say() //我叫李四,今年24了
let person2 = new Person()
person2.say() //我叫张三,今年24了
person2.__proto__.name = '王五' //我叫张三,今年24了
//在这里,person2修改了原型链中的name,由于原型链中数据是共享的,以后创建的实例中name的默认属性也为王五
let person3 = new Person()
person3.say() //我叫王五,今年24了
console.log(Person.prototype)
console.log(person1.__proto__ === Person.prototype) //true
// 这个就是person1实例[[Prototype]]暴露出来的对象,指向构造函数的prototype
console.log(Person.prototype.constructor === Person) //true
//constructor指向的是对象的名字,可以用于判断对象的类型
console.log(person1.constructor === Person) //true
//在实例中,constructor存储了指向与他关联的构造函数
由上面的例子可见,原型链虽然非常好用,但是存在一些的缺点。他弱化了参数能力,所有实例默认获得相同的属性。更严重的是他的属性是所有实例共享的,一旦一个修改了原型链中的内容,所有的实例中原型链中的内容都会被修改,这个可能会导致致命的错误。
原型链和构造函数的组合模式
通过将对象的属性定义在构造函数中,然后把方法和一些公共属性定义在原型链中能很好的解决构造函数中方法重复在实例中创建的问题,也能避免修改原型中的属性,造成更改其他实例的属性的错误。下面请看代码
function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype.sayName = function() {
console.log("My name is " + this.name + " My age is " + this.age)
}
let p = new Person('张三', 20)
p.sayName()//My name is 张三 My age is 20
ES6新增加的类(class)
类的定义和函数一样,有声明式定义和表达式定义
class Person{}//声明式定义
let Person = class{}//表达式定义
注意:类的声明不能提升,如果这样做了会出现未定义错误
console.log(Person) // 报错,不能再声明前使用
//Uncaught ReferenceError: Cannot access 'Person' before initialization
class Person {
name = "张三"//极其不推荐,
}
console.log(new Person().name) //张三
console.log(typeof Person)//function
需要注意的式class本身就是一个特殊的函数,他本质是一个语法糖。
他本质相当于前面的组合模式。方法存在于原型链中,属性存在于对象中。如下图所示
类的构造函数
constructor关键字用于再累代码块中创建构造函数,他会告诉解释器,再使用new创建新的实例的时候,会调用这个方法
console.log(Person) // 报错,不能再声明前使用
//Uncaught ReferenceError: Cannot access 'Person' before initialization
class Person {
constructor(name, age) {
//推荐使用这种方法给函数赋值,使类有了构造函数模式的参数模式(小声蒂固,这本来就是构造寒素)
this.name = name
this.age = age
console.log(name, age)
}
say() {
console.log(this.name)
}
}
let p = new Person('张三', 23) // 默认调用构造函数赋值,并且打印出来 张三 23
p.say() //张三
console.log(p.age) //23
静态方法
ES6的可以再类上定义静态方法。用于执行不特定于实例的操作,即不实例化也能直接调用。但是有一点需要注意,静态方法虽然好用,可不要贪杯哦。因为每个类中只能有一个静态成员(注意,静态成员只有一个,而不是静态方法)
class Person {
constructor(name, age) {
console.log(name, age)
this.name = name
this.age = age
}
say() {
console.log(this.name)
}
static say() {
console.log("我是静态方法")
}
static say2() {
console.log("我是静态方法2")
}
}
Person.say() //我是静态方法
Person.say2() //我是静态方法2
let person = new Person('张三', 24)
person.say() //张三 24