前置知识
JavaScript 中的数据类型可以分为两大类:原始类型和引用类型
原始类型:也叫基本数据类型,有七种,分别是
- string
- number:包括整数和浮点数,以及特殊值如 NaN(Not-a-Number)
- boolean
- undefined
- null
- bigInt:用于表示整数,其长度没有上限
- symbol:一种唯一的、不可变的数据类型
引用类型:也叫复杂类型,主要为对象,其中包括数组、函数体、日期、正则对象等
对象
对象属性的基本操作
var obj = {
name: 'qing',
age: 18,
sex: 'girl',
}
console.log(obj) // 查
以上述代码为例,介绍基于对象的增删改:
①增:
-
obj.abc = 'student'
以 . 的方式添加属性,默认直接以abc字符串为属性名添加到obj上 -
obj[abc] = 'student'
以[ ]的方式添加属性,就是以 变量abc为key的值 为属性添加到obj上 -
obj['abc'] = 'student'
等同于第一种obj.abc = 'student',直接以abc字符串为属性名添加到obj上
② 删:delete obj.a
③ 改:obj.age = 20
与增是相同的手段:对象上已存在该属性就是改,不存在就是增
创建对象的方式
① 创建对象字面量:直接在一个大括号 {}
中定义对象的属性和方法。本文第一段代码就是通过字面量直接创建对象
② 调用系统自带的构造函数 new Object():例var obj = new Object()
③ 调用自定义的构造函数:例如下面这段代码就是自定义了一个People构造函数
function People(name, age) { // 可以向构造函数传参指定对象的属性
this.name = name
this.age = age
}
let p1 = new People('hh', 18) // 实例对象
你有没有感到很神奇:如果这行代码右边如果没有new,只有一个People的函数调用,那变量p1接收到的就是一个undefined,因为People这个函数中并没有return返回值;而现在有了new,p1就接收到的就是一个对象
所以new究竟干了一些什么事情?让我们接下来探讨一下——
new 关键字
- new 会在构造函数中创建一个对象
- 将构造函数的this指向创建的那个对象
- 执行函数中的逻辑代码 (相当于往对象上添加属性)
- 让对象的隐式原型等于构造函数的显式原型
- 返回this对象
所以new一个构造函数可以浅显一点理解成下列代码:
function Person() {
var obj = {}
Person.call(obj)
this.name = 'xxx'
this.age = 18
this._proto_ = Person.prototype
return obj
}
let p = Person('qing', 20)
console.log(p); // { name: 'xxx', age: 18 }
于是你理解了:new一个构造函数会得到一个对象。那么请看下面两段代码:
var num = new Number(123)
console.log(num * 2); // 246
var str = 'abcd'
console.log(str.length); // 4
这里好似产生了两个悖论:①既然num是一个对象,对象怎么能乘以2得出结果246呢?②str是一个字符串,原始类型数据又如何能通过 .
调用length方法呢?
事实上,第一段的确得到了一个实例对象,但是当参与到算术运算时,v8就会把num看成一个原始类型数据;而第二段代码var str = 'abcd'
通过字面量创建对象与调用系统自带的构造函数new String()
无异,当要调用length方法时,在v8(JS引擎)看来str就是一个对象
所以一个东西创建出来可以被当成原始值来用,也可以当成对象来用,取决于你怎么用它。探究背后的原因,就要来聊聊包装类——
包装类
包装类是用于将原始数据类型封装成对象的一组特殊类。JS中有三种主要的包装类,分别对应于三种基本数据类型:Number
, String
, 和 Boolean
。当需要将一个基本类型作为对象使用时,JS引擎会自动创建一个对应的包装类实例,这样就可以在基本类型上使用对象的属性和方法了
包装类的过程
var num = 123
num.abc = 'hello'
console.log(num.abc); // undefined
属性和方法是对象独有的,原始类型数据是不能拥有属性和方法的。而上面这段代码一是给num安排属性不报错,二是访问该属性时也不报错、但是访问结果undefined。你是否又对此感到疑惑?
其实这里发生了一个隐式过程叫做类的包装,过程如下:
new Number(123).abc = 'hello'
delete new Number(123).abc
console.log(num.abc);
通俗点讲,通过字面量创建的num 直接被v8引擎看做成一个由new构造函数创建的对象,等到要访问新增的abc属性时v8才会意识到用户定义的是一个自变量,原来是它自己一厢情愿将num当成一个对象的,所以他就又删除了这个所谓对象上的新增属性abc,最后再打印。访问一个对象身上不存在的属性,结果就是undefined。
同理,下面这段代码要改数组的长度能改的动,但是改字符串的改不动,是因为字符串的执行了包装类的过程
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
arr.length = 2
console.log(arr); // [ 1, 2 ]
var str = 'abcde'
str.length = 2
console.log(str); // abcde
// 字符串的包装类过程
new String('abcde').length = 2
delete new String('abcde').length
new String('abcde').length // 最后一步又会去执行一遍
阿里面试题
最后我们以一道阿里面试题收尾,来测测自己对 对象和包装类知识点 的掌握程度吧
var str = 'abc'
str += 1
var test = typeof(str)
if (test.length == 6) {
test.sign = 'typeof 的返回结果是String'
}
console.log(test.sign);
解析见阿里面试题:有关对象和包装类 - 掘金 (juejin.cn)一文
认真读完后相信你已经对js的对象和包装类知识点有了更加深刻的理解,阅读过程中若发现有任何不足或亮点,欢迎大家在评论区指出交流,我们共同进步~