创建对象
1.1 工厂函数
我们来思考一个问题:如果需要在开发中创建一系列的相似对象,我们应该如何操作呢
比如下面的例子
- 游戏中创建一系列的英雄(英雄具备的特性是相似的,比如都有名字,技能,价格,但是具体的值又不相同)
- 学生系统中创建了一系列的学生(学生都有学号,姓名,年龄等,但是具体的值又不相同)
当然,一种方法是我们创建一系列的对象:
这种方式有一个很大的弊端:创建同样的对象时,需要编写重复的代码
我们是否可以批量创建对象,但是又让它们的属性不一样呢
//一系列学生对象
//采用对象字面量的方式
//重复代码的循环使用
var stu1 = {
name: "why",
height:18,
height:1.88,
running:function() {
console.log("running~")
}
}
var stu2 = {
name:"kobe",
age:30,
height:1.98,
running:function() {
console.log("running~")
}
}
var stu3 = {
name:"james",
age:25,
height:2.05,
running:function() {
console.log("running~")
}
}
我们可以想到一种创建对象的方式:工厂函数
我们可以封装一个函数,这个函数用于帮助我们创建一个对象,我们只需要重复调用这个函数即可
工厂函数其实是一种常见的设计模式
//工厂函数(工厂函数生产student对象) ->一种设计模式
//创建student函数
function createStudent(name,age,height) {
var stu = {}
stu.name = name
stu.age = age
stu.height = height
stu.running = function() {
console.log("running~")
}
return stu
}
var str1 = createStudent("why",18,1.88)//创建的是不同对象
var str1 = createStudent("why",30,1.98)
var str1 = createStudent("why",25,2.05)
1.2 构造函数
我们前面说过,在JavaSript中类的表示形式就是构造函数
- JavaScript中的构造函数是怎么样的?
- 构造函数也是一个普通的函数,从表现形式来说,和千千万万个普通的函数没有任何区别
- 那么如果一个普通的函数被使用了new操作符来调用了,那么这个函数就称之为构造函数
- 如果一个函数被使用new操作符调用了,那么它会执行如下操作
- 在内存中创建一个新的对象
- 这个对象内部的[prototype]属性会被赋值为该构造函数的protopyte属性(涉及到原型)
- 构造函数内部的this,会指向创建出来的新对象
- 执行函数的内部代码(函数体代码)
- 如果构造函数没有返回非空对象,则返回创建出来的新对象
1.2.1 认识构造函数
工厂方法创建的对象有一个比较大的问题:我们在打印对象时,对象的类型都是Object
- 但是从某些角度来说,这些对象应该有一个他们共同的类型
- 下面我们来看一下另一种模式:构造函数的方式
我们先理解什么是构造函数
- 构造函数也称之为构造器(constructor),通常是我们在创建对象会调用的函数(构建对象)
- 在其他面向对象的编程语言里面,构造函数是存在类中的一个方法,称之为构造方法
- 但是JavaScript中的构造函数有点儿不太一样,构造函数扮演了其他语言中的角色
也就是在JavaSript中,构造函数其实就是类的扮演者
- 比如系统默认给我们提供的Date就是一个构造函数,也可以看成是一个类
- 在ES5之前,我们都是通过function来声明一个构造函数(类)的,之后通过new关键字来对其进行调用
- 在ES6之后,JavaScript可以像别的语言一样,通过class来声明一个类
1.2.2 类和对象的关系
那什么是类(构造函数)呢?
- 现实生活中往往是根据一份描述/一个模版来创建一个实体对象的
- 编程语言也是一样,也必须要有一份描述,在这份描述中说明将来创建出来的对象有哪些属性(成员变量)和行为(成员方法)
比如现实生活中,我们会如此来描述一些事物
- 比如水果fruits是一类事物的统称,苹果,橘子,葡萄是其具体的对象
- 比如person是一类事物的统称,而Jim,Lucy,李雷,韩梅梅是具体的对象
比如植物大战僵尸游戏,
//类是对对象的具体描述
function XRK() {
//描述向日葵的属性和方法
生命值
生产阳光()
摇晃()
}
var xrk1 = new XRK()
var xrk2 =new XRK()
function wandou() {
//描述豌豆的属性和方法
生命值
发生子弹()
}
//继承
//总结:构造函数就是一份图纸
1.3 构造函数的函数的其他小技巧
1.3. 函数也是对象
//定义原始类型的变量
var name = "why"//字符串是原始类型
var age = 18
//定义对象类型的变量
//地址 - 指针 - 引用
var obj = {} //堆内存
var foo = function() {} //堆内存
function bar() {} //堆内存
//Function extends Object
console.log(typeof obj)
console.log(typeof foo) // function -> object
var p = new Person()
var stu = new Student() //stu是一个Student ->Person
1.3. 对象额外补充-类方法的编写
//引申一些别的知识
var info = {}
function sayJello() {//这个也是一个对象
}
sayHello.age=18;
console.log(sayHello.age)
function Dog() {
}
//构造函数上(类上面)添加的函数,称之为类方法
Dog.running = funciton(); {}
Dog.running()
1.4 包装类型-原始类型的属性和方法
1.4.1 认识包装类
原始类型的包装类
JavaScript的原始类型并非对象类型,所以从理论上来说,它们是没有办法获取属性或者调用方法
- 但是在开发中,我们经常会这样操作
- 那么,为什么会出现这样奇怪的现象呢?
- 原始类型是简单的值,默认并不能调用属性和方法
- 这是因为JavaScript为了可以使其可以获取属性和调用方法,对齐封装了对应的包装类型
var name = "Hello World"
// var name = "why"
var height = 1.88888888
function String(str) {//先转化成一个String类型的对象
this.str = str
this.length = 11
this.split = function() {
}
}
name = new String(name)
name.length
name.split("")//用完之后JS引擎会自动销毁掉
常见的包装类型有:String、Number、Boolean、Symbol、BigInt类型
包装类型的使用过程
默认情况下,当我们调用一个原始类型的属性或者方法时,会进行如下操作
- 根据原始值,创建一个原始类型对应的包装类型对象
- 调用对应的属性和方法,返回一个值
- 创建的包装对象被销毁
- 通常JavaScript引擎会进行很多的优化,它可以跳过创建包装类的过程在内部直接完成属性的获取或者方法的调用
我们也可以自己来创建一个包装类的对象
- name1是字面量(literal)的创建方式,name2是new创建对象的方式
//在调用原始类型的属性或者方法时,内部的操作 name = new String(name)
console.log(height.toFixed(2))
//明明你是一个基本数据类型,你不应该有方法的,属性的
console.log(name.length)
console.log(name.split(""))
var obj = {
name:"kobe",
running:function() {
}
}
var name1 = new String("Hellow World")
console.log(typeof name,typeof name1)
注意事项:null和undefined没有任何的方法,也没有对应的“对象包装类”