JavaScript 基础
初识 JavaScript
JS 的作用
1995年,网景公司(Netscape)的员工布兰登·艾奇(Brendan Eich)用 10 天时间就把 Javascript 设计出来了,最初命名为 LiveScript,后来在与 Sun 合作之后将其改名为 JavaScript。
JavaScript 是世界上最流行的语言之一,是一种运行在客户端浏览器的脚本语言(Script 是脚本的意思),常常把 JavaScript 简称为 JS。
JS 编写出来的程序不需要编译就可以直接运行,运行过程中由 JS 解释器(JS 引擎)逐行进行解释并执行。
如今 JavaScript 的使用场景越来越广泛,除了用在浏览器中来和用户进行交互,在其他领域也有广泛的应用。
- 表单动态校验(密码强度检测) (JS 最初产生的目的)
- 网页特效
- 服务端开发(Node.js)
- 桌面程序(Electron)
- 移动 App(Cordova)
- 控制硬件-物联网(Ruff)
- 游戏开发(cocos2d-js)
浏览器中的 JS
浏览器分成两部分:渲染引擎和 JS 引擎。
- 渲染引擎:用来解析 HTML 与 CSS,俗称内核,比如新版 Chrome 浏览器的 Blink 内核,Safari 浏览器和早期 Chrome 浏览器所使用的 Webkit 内核。
- JS 引擎:也称为 JS 解释器,用来读取网页中的JavaScript代码,对其处理后运行,比如 Chrome 浏览器的 V8 引擎。
浏览器内核本身并不会执行 JS 代码,而是通过内置 JavaScript 引擎(解释器)来执行 JS 代码。JS 引擎执行代码时逐行解释每一句源码,将其转换为机器语言,然后交由计算机去执行。因为是因为 JS 是逐行解释执行的,类似于演戏时用到的脚本——演员看了指令就知道自己该表演什么,说什么台词;计算机看了指令就知道自己该做什么事情,所以我们把 JS 归为脚本语言。
JS 的组成
广义上的 JS 的组成:
- ECMAScript:ECMAScript 规定了 JS 的编程语法和基础核心知识,是所有浏览器厂商共同遵守的一套 JS 语法工业标准。JavaScript(网景公司)和 JScript(微软公司)是 ECMAScript 语言的具体实现,并在此基础做了各自特有的扩展。
- DOM:文档对象模型(Document Object Model,简称DOM),是 W3C 组织推荐的用来处理可扩展标记语言(XML)的标准编程接口。通过 DOM 提供的接口可以对页面上的各种元素进行操作(大小、位置、颜色等)。
- BOM:浏览器对象模型(Browser Object Model,简称 BOM),它提供了独立于内容的、可以与浏览器窗口进行互动的对象结构。通过 BOM 可以操作浏览器窗口,比如弹出框、控制浏览器跳转、获取分辨率等。
JS 的位置
JS 有 3 种书写位置,分别为行内、内嵌和外部。
行内式 JS:可以将单行或少量 JS 代码写在 HTML 标签的事件属性值中(大多以 on 开头的属性,如:
onclick
)。在 HTML 中编写大量 JS 代码时,不方便阅读,并且单双引号多层嵌套匹配时,非常容易弄混,一般只在特殊情况下使用行内式 JS。注意单双引号的使用:在 HTML 中推荐使用双引号将键的值括起来,JS 中推荐使用单引号将字符串括起来。1
<input type="button" value="点我试试" onclick="alert('Hello World');" />
内嵌 JS:可以将多行 JS 代码写到
<script>
标签中,内嵌 JS 是学习 JS 时常用的书写位置,比较方便。1
2
3<script>
alert('Hello World~!');
</script>外部 JS文件:通过
<script>
标签直接引用外部的 JS 文件,适合于 JS 代码量比较大的情况。注意:引用外部 JS 文件的<script>
开闭标签中间不可以再写任何 JS 代码。1
<script src="my.js"></script>
JS 的输入输出语句
为了方便信息的输入输出,JS 提供了一些输入输出语句与浏览器用户或者开发者进行交互。
alert(msg)
:浏览器弹出警示框,主要用来显示消息给浏览页面的用户。console.log(msg)
:浏览器控制台打印输出信息,主要用来给程序员自己查看 JS 运行时的消息或者中间结果来更好的调试程序。prompt(info)
:浏览器弹出输入框,用户可以通过输入框键入一些信息。
变量
变量的使用
变量在使用时分为两步:
声明变量:
var
(variable,变量的意思)是一个 JS 关键字,用来声明变量。使用该关键字声明变量后,计算机会自动为变量分配内存空间。1
var age; // 声明一个 名称为 age 的变量
赋值:把右边的值赋给左边的变量空间中 。
1
age = 10; // 给 age 这个变量赋值为 10
也可以在声明一个变量的同时并赋值,我们称之为变量的初始化。JS 引擎在预解析到初始化语句时,会将其拆成声明和赋值两部分,声明置顶,赋值保留在原来位置。
1 |
|
同时声明多个变量时,只需要写一个 var
, 多个变量名之间使用英文逗号隔开。
1 |
|
不要使用 =
号给多个变量同时赋值,可能会造成意料之外的结果。
1 |
|
声明变量特殊情况
只声明一个变量,不赋值就直接使用,变量的值默认为的 undefined
。
1 |
|
没有声明变量,也没有赋值就直接使用,浏览器控制台会报错 xxx is not defined,并且后面的 JS 代码得不到执行,因为 JS 是逐行解释执行的,某一行报错就无法接着执行后面的语句。
1 |
|
不声明变量,直接就给变量赋值,可以正常使用变量,不会报错,但这个变量会被当做隐式全局变量(implicit globals),拥有全局作用域。
1 |
|
JS 允许重复声明同一个变量,并且不会导致之前声明的变量的值的丢失,但是如果后一次声明的同时赋值会导致值被覆盖。参考:MDN:Statement - Var
1 |
|
变量命名规范
标识符:就是指开发人员为变量、属性、函数、参数取的名字。标识符不能是关键字或保留字。
关键字:是指编程语言已经规定好某些特殊用途的单词,不能再用它们充当变量名、方法名。 JS 中的关键字包括:break、case、catch、continue、default、delete、do、else、finally、for、function、if、in、instanceof、new、return、switch、this、throw、try、typeof、var、void、while、with 等。
保留字:实际上就是预留的“关键字”,意思是现在虽然还不是关键字,但是未来可能会成为关键字,同样不能使用它们当变量名或方法名。包括:boolean、byte、char、class、const、debugger、double、enum、export、extends、final、float、goto、implements、import、int、interface、long、native、package、private、protected、public、short、static、super、synchronized、throws、transient、volatile 等。
注意:如果将保留字用作变量名或函数名,那么除非将来的浏览器实现了该保留字,否则很可能收不到任何错误消息。当浏览器将其实现后,该单词将被看做关键字,如此将出现关键字错误。
了解上面三个概念后,变量名规范实际上就是标识符规范。
- 变量名由字母(A-Z、a-z)、数字(0-9)、下划线(_)、美元符号( $ )组成,但是不能以数字开头。如:usrAge,num01, _name,$name,是正确的变量名。可以使用在线工具 JS 变量名验证器判断一个变量名是否合法。
- 虽然 ECMAScript 规定所有 Unicode letter 均可以作为变量名标识符,但是尽量不要使用中文做标识符。参考:Why aren’t ◎ܫ◎ and ☺ valid JavaScript variable names?
- 变量名严格区分大小写。
var app;
和var App;
声明了两个不同的变量。 - 变量名不能是关键字、保留字。例如:var、for、while。
- 不要使用 name、location、self 作为全局变量名(global variable),这些变量名已经被浏览器的
windows
对象的属性所使用,更多浏览器的属性参考这个链接。 - 变量名建议遵守驼峰命名法。首字母小写,后面单词的首字母需要大写,例如 myFirstName。
数据类型
JavaScript 是一种动态类型语言,不需要提前声明变量的类型,在程序运行过程中,类型会被自动确定,意味着同一个变量在不同时刻还类型可能不一样。参考:弱类型、强类型、动态类型、静态类型语言的区别是什么?
1 |
|
JS 把数据类型分为两类:
- 简单数据类型(number,string,boolean,undefined,null),又叫做基本数据类型(primitive data type)或者值类型,在存储时数据的值直接存放在栈空间中,因此叫做值类型。
- 复杂数据类型(object),又叫做引用类型,引用类型变量在存储时栈空间中存储的仅仅是引用地址,用来指向一个对象实例,真正的对象实例存放在堆空间中,因此叫做引用数据类型。
数字型 number
JavaScript 数字类型既可以用来保存整数值,也可以保存小数(浮点数)。在 JS 中数值字面量前面加 0 表示八进制,字面量前面加 0x 表示十六进制。
1 |
|
JavaScript 中数值的一些特殊值。
1 |
|
可以使用 isNaN()
方法来判断一个变量是否为非数字类型。isNaN(x)
返回 false
说明 x 为数字,返回 true
说明 x 不是数字。
字符串型 string
字符串型的字面量可以是 双引号 ""
和 单引号 ''
的括起来的任意文本,因为 HTML 标签里面的属性使用的是双引号,JS 里我们更推荐使用单引号。当文本中也有引号出现时注意引号的匹配,可以用单引号嵌套双引号 ,或者用双引号嵌套单引号,或者使用下面的转义符号。
类似 HTML 里面的特殊字符,字符串的文本中也有特殊字符,我们称之为转义符。转义符都是反斜杠 \
开头的,常用的转义符及其说明如下:
转义符 | 解释说明 |
---|---|
\n | 换行符,n 是 newline 的意思 |
\ \ | 反斜杠 \ |
' | ‘ 单引号 |
" | ” 双引号 |
\t | tab 缩进 |
\b | 空格 ,b 是 blank 的意思 |
字符串是由若干字符组成的,这些字符的数量就是字符串的长度。通过字符串的 length
属性可以获取整个字符串的长度。
多个字符串之间可以使用二元操作符 +
进行拼接,其拼接方式为 字符串 + 任何类型 = 拼接之后的新字符串, 拼接前会把与字符串相加的任何类型转成字符串,再拼接成一个新的字符串。
1 |
|
布尔型 boolean
布尔类型只有两个值:true
和 false
。布尔型和数字型相加的时候, true 的值为 1 ,false 的值为 0。
1 |
|
undefined 和 null
一个变量声明后没有被赋值会有一个默认值 undefined
,如果和字符串相连拼接,这个变量被视为 ‘undefined’ 字符串,和数字或者布尔型进行数学运算结果会是 NaN
。
1 |
|
一个变量赋值 null
,即里面存的值为空,表示一个空对象引用,常用来主动释放一个变量引用的对象,表示一个变量不再指向任何对象地址。如果和字符串相连拼接,被视为 ‘null’ 字符串,和数字或者布尔型进行数学运算被当作数值 0 参加运算。
获取变量类型
可以使用 typeof
操作符来检测变量的数据类型,返回一个字符串。
undefined
和 null
都是只有一个值的特殊类型。typeof
一个没有赋值的变量会返回 undefined
,由于某些历史遗留原因,用 typeof
检测 null
返回是 ‘object’,参考Why is typeof null “object”?。
数据类型转换
编程中经常要把一种数据类型的变量转换成另外一种数据类型。例如通过 form 表单、prompt()
获取过来的数据默认是字符串类型的,需要转换成数字型,才能进行数学运算。
在浏览器控制台中黑色的输出代表是一串字符,蓝色的代表非字符串。
通常会出现 3 种类型的转换:
转换为字符串类型
转换为数字型
转换为布尔型
只有代表空、否定的值会被转换为false
,例如''
、0
、NaN
、null
、undefined
, 其余值都会被转换为true
。1
2
3
4
5
6
7console.log(Boolean('')); // false
console.log(Boolean(0)); // false
console.log(Boolean(NaN)); // false
console.log(Boolean(null)); // false
console.log(Boolean(undefined)); // false
console.log(Boolean('false')); // true
console.log(Boolean(12)); // true
操作符
算术运算符
和其他大多数编程语言一样,也是加减乘除取模五种基本的算术运算符。注意:和 Java 不同,JS 的整数除法结果可以是浮点数,可以使用 parseInt()
给商取整。
和大多数计算机语言一样,浮点数的存储和计算存在精度丢失问题。不要直接判断两个浮点数是否相等 !
1 |
|
递增和递减运算符
和其他语言一样,也存在前置递增递减和后置递增递减的区别,不再赘述。
比较运算符
比较运算符(关系运算符)是两个数据进行比较时所使用的运算符,比较运算后,会返回一个布尔值(true
/ false
)作为比较运算的结果。
需要注意的是,如果将字符串与数字进行比较,除了 ===
运算符 ,JavaScript 会把字符串隐式转换为数值型再进行比较。空字符串将被转换为 0。非数值字符串将被转换 NaN
,比较结果始终为 false
。
比较两个字符串时,从第一个字符往后按字母顺序进行比较。
1 |
|
逻辑运算符
逻辑运算符是用来进行布尔值运算的运算符,其返回值也是布尔值。
&&
和 ||
都是短路的。短路:左边的表达式值可以确定逻辑运算的结果时,就不再继续运算右边的表达式。
短路与:表达式1 && 表达式2,如果第一个表达式的值为真,则返回表达式 2,如果第一个表达式的值为假,则返回表达式 1。
短路或:表达式1 || 表达式2,如果第一个表达式的值为真,则返回表达式 1,如果第一个表达式的值为假,则返回表达式 2。
如果表达值式的值不是布尔量会隐式转换为布尔量进行逻辑运算,但是最终返回的是表达式的值本身,而不是表达式所转换得到的布尔量。
1 |
|
赋值运算法
运算符优先级
流程控制
JS 也有 if-else
,switch-case
两种分支控制语句,for
,while-do
,do-while
三种循环控制语句,语法和 C 语言和 Java 一致,不作介绍。唯一需要注意的是 switch
后面跟的表达式可以是字符串或者数值型,与 case
后面的表达式进行的是全等匹配(===
)。
数组
创建数组
JS 中创建数组有两种方式:
利用关键字
new
创建数组。1
var arr = new Array(); // 创建一个新的空数组
利用 [] 括起来的数组字面量来创建数组,声明数组的同时并给数组元素赋值。
1
2var arr = []; //使用数组字面量方式创建空的数组
var strs = ['a','b','c']; //使用数组字面量方式创建带初始值的数组
和 Java 和 C 语言显著不同的是,由于 JS 是动态类型语言,同一个数组中可以存放任意类型的数据,例如字符串,数字,布尔值等。
1 |
|
访问数组
可以通过数组索引(下标)来访问数组元素的序号,数组下标从 0 开始。
使用数组的 length
属性可以访问数组元素的数量(数组长度)。配合 for
循环可以索引遍历数组中的每一项。
1 |
|
length
属性是可读写的,当我们数组里面的元素个数发生了变化,这个 length
属性自动跟着一起变化。
可以通过修改 length 长度来给数组扩容,扩容产生的新增元素默认值是 undefined
。
1 |
|
也可以通过索引范围外的数组元素赋值来扩容数组,JS 中不存在索引越界错误。
1 |
|
使用 length
属性作数组索引给元素数组元素赋值,可以实现数组动态增长。
1 |
|
函数
声明函数
函数的两种声明方式:
自定义函数方式(命名函数):利用函数关键字
function
声明函数。因为预解析的存在,调用函数的代码既可以放到声明函数的前面,也可以放在声明函数的后面。由于函数一般是为了实现某个功能才定义的, 所以通常我们将函数名命名为动词,比如 getSum,同样函数名也属于标识符,要遵循前面提到的标识符规范。1
2
3
4function fn() {
... //函数体代码
}
fn(); //通过函数名调用,调用函数的代码既可以放到声明函数的前面,也可以放在声明函数的后面函数表达式方式(匿名函数):声明一个变量,变量里面存储的是一个函数对象,函数本身是没有名字的(匿名),只能通过变量名来调用函数,函数调用的代码必须写到函数体后面。
1
2
3
4var fn = function(){
...
}; // 这是函数表达式写法,匿名函数后面跟分号结束
fn(); // 通过变量名调用函数,函数调用必须写到函数体下面
调用函数
声明函数本身并不会执行代码,只有调用函数时才会执行函数体代码。
JS 中不会根据实参和形参列表的匹配情况进行函数重载,可能存在函数形参和实参个数不匹配的问题。
1 |
|
执行到函数体中的 return
语句时,函数会停止执行,并返回指定的值。如果函数没有 return
语句,执行完整个函数体后,返回的值是 undefined
。return
只能返回一个值。如果用逗号隔开多个返回值,则实际上一个逗号表达式,整个逗号表达式的值是最后一个表达式的值。但在函数调用时,各个实参之间是用逗号隔开的,参数列表中的逗号就不是逗号运算符。
当我们不确定有多少个参数传递的时候,可以用 arguments
对象来获取。在 JavaScript 中,arguments
是当前函数的一个内置对象。所有函数都内置了一个 arguments
对象,arguments
对象中存储了传递的所有实参。arguments
展示形式是一个伪数组,可以通过数组索引获取第几个参数,也可以配合 length
属性来遍历所有实参,但是不能使用普通数组中的 push()
, pop()
等方法。
函数的形参也可以看做是一个变量,当把一个值类型变量作为参数传给函数的形参时,其实是把变量在栈空间里的值复制了一份给形参,那么在方法内部对形参做任何修改,都不会影响到的外部变量。当把引用类型变量传给形参时,其实是把变量在栈空间里保存的引用地址复制给了形参,形参和实参指向的是同一个堆地址,所以操作的是同一个对象实例。
作用域
JavaScript 在 ES 6 标准前只有以下两种作用域,没有块级作用域:
- 全局作用域:作用于所有代码执行的环境(整个
<script>
标签内部)或者一个独立的 JS 文件。 - 局部作用域:作用于函数内的代码环境,就是局部作用域。 因为跟函数有关系,所以也称为函数作用域。
在 JavaScript 中,根据作用域的不同,变量可以分为两种:
- 全局变量:在函数外部通过
var
声明的变量或者任意位置不用var
声明就直接使用的变量,后一种是隐式全局变量(implicit globals),实际上是window
对象的一个属性,可以使用delete
操作符删除这些属性,虽然也全局作用域可见,但是和真正的全局变量有一点差异,一般不建议使用。全局变量拥有全局作用域,在任何一个地方都可以使用,只有在浏览器标签页面关闭时才会被销毁,因此比较占内存。 - 局部变量:在函数内部通过
var
声明的变量,函数的形参实际上也是局部变量。局部变量拥有局部作用域,只能在该函数内部使用,当函数运行结束后,就会被销毁,因此更节省内存空间。
作用域链
如果函数声明中还有另外的函数声明,那么在这个局部作用域中就又诞生一个局部作用域,根据在内部函数可以访问外部函数变量的这种机制,用链式查找决定哪些数据能被内部函数访问,这种机制就称为作用域链。
示例 1:
1 |
|
示例 2:
1 |
|
示例 3:
1 |
|
预解析
JavaScript 代码是由浏览器中的 JavaScript 解析器来执行的。JavaScript 解析器在运行 JavaScript 代码的时候分为两步:预解析和代码执行。
- 预解析:在当前作用域下,JS 代码执行之前,浏览器会默认把通过
var
声明的变量和通过function
声明的函数在内存中进行提前声明。 - 代码执行: 从上到下顺序解释执行 JS 语句。
建议始终在作用域顶部声明变量(全局代码的顶部和函数体代码的顶部),这可以清楚知道哪些变量是全局作用域,哪些变量是局部作用域。
变量预解析
变量提升(Hoisting): 变量的 var
声明语句会被提升到当前作用域的最上面,变量的赋值语句不会提升。
1 |
|
经过预解析后等效为:
1 |
|
函数预解析
函数提升: 命名函数的 function
声明语句会被提升到当前作用域的最上面,因此在函数声明之前就可以调用函数。
1 |
|
经过预解析后等效为:
1 |
|
匿名函数的声明是通过声明变量实现的,进行的实际上是变量提升,因此在没有让变量真正地引用一个函数对象之前,不能调用函数。
1 |
|
经过预解析后等效为:
1 |
|
对象
创建对象
在 JavaScript 中,现阶段我们可以采用三种方式创建对象(object):
利用字面量创建对象,可以用花括号
{}
括起来的键值对的形式表示对象,冒号前面的键是对象的属性名或者方法名,冒号后面的值是属性值或者方法体。1
2
3
4
5
6
7
8var star = {
name : 'pink',
age : 18,
sex : '男',
sayHi : function(){
alert('大家好啊~');
}
};利用
new Object()
创建对象,然后给对象不存在的属性赋值,就相当于给对象动态地添加属性或者方法。1
2
3
4
5
6
7var andy = new Obect();
andy.name = 'pink';
andy.age = 18;
andy.sex = '男';
andy.sayHi = function(){
alert('大家好啊~');
}利用构造函数创建对象:构造函数是一种特殊的函数,主要用来创建某一类对象并初始化,即为对象成员变量赋初始值,它总与
new
操作符一起使用。我们可以把对象中一些公共的属性和方法抽取出来,然后封装到这个函数里面。构造函数的首字母通常要大写,函数内的属性和方法前面需要添加this
关键字,this
指向正在创建的对象,构造函数里面的代码可以给这个新对象添加属性和方法。使用new
调用构造函数会自动返回所创建的新对象,不需要使用return
语句返回。构造函数抽象了对象的公共部分,封装到了函数里面,可以用来泛指某一大类(class),通过new
关键字创建对象的过程我们也称为对象实例化。1
2
3
4
5
6
7
8
9
10
11
12function Person(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
this.sayHi = function() {
alert('我的名字叫:' + this.name + ',年?:' + this.age + ',性?:' + this.sex);
}
}
var bigbai = new Person('大白', 100, '男');
var smallbai = new Person('小白', 21, '男');
console.log(bigbai.name);
console.log(smallbai.name);
使用对象
可以通过 .
或者 []
来访问对象里面的属性,方括号里面只能是字符串,属性名必须先用引号括起来成字符串常量。可以通过 对象.方法名()
来调用对象的方法,方法名字后面一定加括号。
1 |
|
for-in
语句用于对对象的所有属性进行操作,语法中的变量名可以是任何符合命名规范的标识符 ,通常我们会将这个变量写为 k 或者 key。for-in
语句也可以用来遍历数组中的所有元素,这时候 k 代表数组索引。
1 |
|
JS 内置对象
JavaScript 中的对象分为 3 种:自定义对象、内置对象、浏览器对象,前面两种对象属于 ECMAScript 规范,浏览器对象属于 JS 独有。内置对象就是指 JS 语言自带的一些对象,这些对象提供了一些常用的功能(属性和方法),帮助开发者快速开发。
Math
1 |
|
Date
注意:getMonth()
和 getDay()
返回的值是以 0 开始计数。
1 |
|
Date 对象需要实例化后才能使用,如果调用构造函数 Date()
不写参数,就返回执行到 new
语句时的当前的系统时间,如果 Date()
里面写参数,就返回参数里面的时间。例如日期格式字符串为 ‘2019-5-1’
,可以写成 new Date('2019-5-1')
或者 new Date('2019/5/1')
1 |
|
Date 对象中存储了某一时刻距离 1970年1月1日(世界标准时间)起的毫秒数,经常利用这个数值来进行天、时、分、秒的换算。可以利用 valueOf()
和 getTime()
方法来得到这个数值,也可以使用一元操作符 +
。
1 |
|
Array
1 |
|
下面四个方法用来添加删除数组元素。
reverse()
,sort()
对整个数组元素反转和排序。
sort()
需要传入一个比较策略,根据传入的函数对象来排序。
1 |
|
indexOf()
,lastIndexOf()
用来查找某一个元素元素在数组中的位置。
toString()
, join()
用来把数组转换为字符串。
concat()
,slice()
,splice()
用来连接数组和删除数组。
String
不可变性:字符串对象的值不可变,虽然看上去可以给字符串对象重新赋值,但其实是引用对象指向了内存中新开辟的空间,这个空间中存放了新的字符串对象。由于字符串的不可变,字符串所有的方法,都不会修改字符串本身,而是返回一个新的字符串,因此在大量拼接字符串的时候会有效率问题。可以先将需要拼接的字符串依次传入一个数组,然后使用 array.join('')
把整个数组的字符串拼接,可以大大提高效率。
indexOf()
,lastIndexOf()
方法用于查找字符串中的字符出现位置。
charAt()
,charCodeAt()
,str[]
根据索引返回字符。
concat()
,substr()
,slice()
,substring()
方法用于字符串连接和截取。
replace()
方法可以进行简单的字符串替换,支持使用正则表达式进行匹配。
1 |
|
split()
方法用于切分字符串,它可以将字符串切分为字符串数组。
1 |
|
包装类型
为了方便操作基本数据类型,JS 提供了基本类型的包装类型,用来把简单数据类型包装成为复杂数据类型,这样基本数据类型就有了属性和方法。除了 null
和 undefined
,其他的基本类型都有对应的包装类型。例如 string,number,boolean,对应了String
、Number
和 Boolean
三种基本包装类型。
1 |
|
按道理基本数据类型是没有属性和方法的,对象才有属性和方法,但上面的代码却不会报错,这是因为 JS 会把基本数据类型变量自动包装成复杂数据类型:先创建临时的包装类型对象,然后再访问属性或者方法,其执行过程如下 :
1 |
|
访问完属性或者方法之后,就立即销毁该对象,这就意味着在运行时为基本类型值添加属性和方法是没有意义的。
1 |
|
参考
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!