js笔记

Last updated on a day ago

基础语法

Hello World

1
2
3
4
5
<head>
<script>
alert("hello world")
</script>
</head>

代码运行

JavaScript书写位置

共有三种写法
行内script外部js文件中

行内

新建一个 haicoder.html 文件,并输入以下内容:

1
<input type="button" value="haicoder" onclick="alert('Hello HaiCoder')" />

代码运行
当我们点击按钮时,运行onclick中的代码

script标签

1
2
3
4
5
<head>
<script>
alert('HelloWorld');
</script>
</head>

代码运行

使用外部js文件

新建一个main.js文件

1
2
3
function Hello() {
alert("HelloWorld js");
}

然后再新建一个main.html文件

1
2
3
4
5
6
<head>
<script type="text/javascript" src="main.js"></script>
<script type="text/javascript">
Hello()
</script>
</head>

代码运行

JavaScript是弱类型语言

JavaScript 是弱类型语言,因此 JavaScript 变量 无须声明可以直接赋值。
并且 JavaScript 变量的 数据类型 可以动态改变,即同一个变量可以一会儿被赋值为 整数值,一会儿也可以被赋值为字符串。

变量

JavaScript 是弱类型语言,因此 JavaScript 变量无须声明可以直接赋值。

变量声明

在 JavaScript 中 ,声明变量使用 var 关键字,var 关键字后面直接加变量名即可。

1
var name;

声明并复制

1
var name = "lihua";

多个声明变量并赋值

1
var name = "lihua", age = 100;

改变类型

JavaScript是弱类型语言,变量的数据类型可以改变

1
2
3
4
5
6
<script>
var num = 10;
console.log(num)
num = "hi"
console.log(num)
</script>

代码运行
按下f12打开浏览器控制台,查看结果

变量命名规则和规范

规则

  1. JavaScript 变量只能由字母、数字、下划线、$ 符号组成,不能以数字开头。
  2. JavaScript 变量命名时不能使用 关键字 和保留字,例如:for、while。
  3. JavaScript 变量命名是区分大小写的,即变量 a 与变量 A 是两个不同的变量。

规范和建议

遵守驼峰命名法。首字母小写,后面单词的首字母需要大写。例如:userName、userPassword 等。
建议:
在给变量命名时,我们要做到见名知意,同时,我们最好也能做到见到变量名,能知道变量的 数据类型 ,因此,在定义变量时也有一套类似的常识标准,即,我们使用不同的变量前缀,代表变量的不同数据类型,如下:

类型 前缀 类型 实例
数组 a Array aItems
布尔 b Boolean bIsPass
浮点数 f Float fPrice
函数 fn Function fnHandler
整数 i Integer iCount
对象 o Object oPerson
正则表达式 re RegExp reEmailCheck
字符串 s String sUserName

js关键字

数据类型

基本数据类型

JavaScript 的基本数据类型有六种,即:字符串(String)数字(Number)布尔(Boolean)空(Null)未定义(Undefined)代表(Symbol)
其中代表是在 ECMAScript 6 中新添加的类型。一种实例是唯一且不可改变的数据类型。
字符串不能改变
其中数值、字符串、布尔称为原始类型,是最基本的数据类型,不能再细分。
同时,这些类型又有值类型引用类型

  • 值类型:Number、String、Boolean、null、undefinded
  • 引用类型:函数、对象、数组等
    值类型传递的是值,比如将变量a赋值给变量b,只传递值的话,两者只是单纯的值相等,没有其他关联,改变其中一个对另一个没有影响。如:
    1
    2
    3
    4
    5
    var a = 100;//Number类型,值传递
    var b = a;//赋值
    b = 10;
    console.log(a);//将a输出到浏览器控制台,100
    console.log(b);//将b输出到浏览器控制台,10
    而如果是引用类型,比如数组,则对另一个的修改会影响另一个
    1
    2
    3
    4
    5
    var c = new Array(1,2,3);//声明数组[1,2,3]
    var d = c;
    d[0] = 0;[将d数组的1改为0]
    console.log(c);//输出[023]
    console.log(d);//输出[023]

JavaScript复杂数据类型

JavaScript 的复杂数据类型只有一种,即 Object。对象则称为合成类型(complex type)的值,因为一个对象往往是多个原始类型的值的合成,可以看作是一个存放各种值的容器。

字符串

JavaScript 中的字符串是一串表示文本值的不可变的字符序列,JavaScript 的字符串使用** String 对象**来表示。声明 JavaScript 字符串语法:

数字

JavaScript 中的数字可以分为整数和 浮点数 类型,JavaScript 的数字使用 Number 对象来表示。JavaScript 的整数还可以分为二进制、八进制、十进制和十六进制形式。
进制声明

1
2
3
4
5
6
7
8
var num1 = 16;//10进制
var num2 = 0b1000;//2进制
var num3 = 0100;//8进制,严格模式下不允许使用
var num4 = 0x1;//16进制
console.log(num1)
console.log(num2)
console.log(num3)
console.log(num4)

代码运行

浮点数

JavaScript 的浮点数使用 Number 对象来表示。JavaScript 的浮点数有两种表现形式,第一种就是用普通的小数表示法、第二种就是使用科学计数法

数字范围

JavaScript 数字的最大值和最小值都可以使用 Number 里面的常量值来获取。

1
2
3
4
5
6
7
8
//最大值
Number.MAX_VALUE
//最小值
Number.MIN_VALUE
//无穷大
Infinity
//无穷小
-Infinity

代码运行

NaN

JavaScript 中 NaN 属性是代表非数字值的特殊值,该属性用于指示某个值不是 数字。可以把 Number 对象设置为该值,来指示其不是数字值。
NaN 与任何值都不相等,包括其本身。也就是说NaN==NaN是false
同时将一个非数字的变量转为数字,会返回NaN
isNaN函数:用于判断传入的 变量 是否不是一个 数字,如果是一个数字,则返回 false,否则,返回 true。

1
2
3
4
5
var num = "string";
var numStr = "123"
console.log(Number(num));//num无法转换为数字,返回NaN
console.log(NaN==NaN);//NaN不等于任何,包括自身,返回false
console.log(isNaN(numStr));//numStr可以转换成数字,返回false

代码运行

布尔

JavaScript 中的 true 在内部存储的值是 1,JavaScript 中的 false 在内部存储的值是 0。

1
2
console.log(true == 1);
console.log(false == 0);

代码运行

undefined

JavaScript 中的 Undefined 表示 变量 未定义时的属性,当读取不存在的 对象 属性时会返回 undefined。

1
2
3
4
var t1 = "";//声明变量并赋值
var t2;//之声明未赋值
console.log(t1 == underfined);//false
console.log(t2 == underfined);//true

代码运行

null

JavaScript 中的 null 是一个表明 null 值的特殊 关键字。 JavaScript 是大小写敏感的,因此 null 与 Null、NULL 或变体完全不同。
JavaScript 中的 null 是可以进行比较的,即 null 等于 null。

1
2
3
4
var t1 = "";//赋值空字符串
var t2 ;//不赋值
console.log(t1 == null)//false
console.log(t2 == null)//true

代码运行

null与undefined

JavaScript 中的 null 是 js 中的 关键字,表示空值,null 可以看作是 was object 的一个特殊的值,如果一个 object 值为空,表示这个对象不是有效 对象。
undefined 不是 js 中的关键字,其是一个 全局变量,是 Global 的一个属性。
相同点:都是原始类型的值,保存在栈中变量本地。
不同

  1. JavaScript null 与 undefined 类型 不一样。
  2. 转化为值时不一样,undefined 为 NaN ,null 为 0。
    返回underfined的情况
  3. 使用了一个未定义的变量。
  4. 使用了已定义但未声明的变量。
  5. 使用了一个对象属性,但该属性不存在或者未赋值。
  6. 调用 函数 时,该提供的参数没有提供。
  7. 函数没有返回值时,默认返回 undefined。

Symbol类型

JavaScript 中的 Symbol 是 ECMAScript 6 中新添加的 类型,是一种实例是唯一且不可改变的数据类型。

1
2
3
var v1 = Symbol();						//声明Symbol变量
console.log("v1 = ",v1); //返回Symbol()
console.log("typeof(v1) = ",typeof(v1));//v1的类型为symbol

而且每个Symbol()变量都是唯一的

1
2
3
4
var v2 = Symbol("hello");
var v3 = Symbol("hello");
console.log("v1==v2:"v1 ==v2); //false
console.log("v2==v3:",v2 == v3);//false

同时也正因为这个性质,可以用作对象属性

1
2
3
4
5
6
7
8
9
10
const PROP_NAME = Symbol();         //声明常量,下同
const PROP_AGE = Symbol();
var obj = { //声明名为obj的对象
[PROP_NAME]:"HaiCoder" , //使用PROP_NAME的值作为属性名,下同
[PROP_AGE] : 109
};
arr = Object.getOwnPropertySymbols(obj);//获取obj中symbol类型的属性,放入数组
console.log("arr = ",arr); //{Symbol(),Symbol()}
console.log("arr[0] = ",arr[0]); //Symbol()
console.log("arr[1] = ",arr[0]); //Symbol()

代码运行

数据类型转换

toString 转换成字符串类型 x.ToString()
String 转换成字符串类型 String(x)
Number 转换成数字类型 Number(x)
parseInt 转换成整数类型 x.parseInt()
parseFloat 转换成浮点数类型 x.parseFloat()
Boolean 转换成布尔类型 Boolean(x)
#### 转字符串

将特定的数据类型变量转成字符串类型可以使用toString()和String方法。有些数据类型没有toString()方法,比如undefined和null,这时候需要用string方法

语法:

1
2
x.toString();//将变量x转换成字符串类型。返回转换成功后的字符串
string(x); //将变量x转换成字符串类型。

转数字

将特定的数据类型变量转成数字类型可以使用Number()和parseInt()方法

基本语法:

1
2
parseInt(x,radix);//将变量 x 转换成数字类型。返回转换成功后的指定进制数字,进制范围【2,36】
Number(x); //将变量 x 转换成数字类型。

同时转换规则两者也有一定的差别:

当Number遇到无法转换成的字符串比如“123b“会返回NaN,而parseInt则会转换到无法转换为止,即返回123,当radix参数超过范围时,返回NaN

1
2
3
4
5
6
7
var str = "123b";
var strToNum1 = parseInt(str,10);
var strToNum2 = parseInt(str,37);
var strToNum3 = Number(str);
console.log("parseInt(str,10) = ",strToNum1);
console.log("parseInt(str,37) = ",strToNum2);
console.log("Number(str) = ",strToNum3);

代码运行
与parseInt类似的还有parseFloat()

转布尔

其他类型布尔使用Boolean函数。规则如下:

类型 规则
Number 0为false,非0true
string 空为false,其他true
null false
underfined false

this指向

  1. 当以函数的形式调用时, this是window。
  2. 当以方法的形式调用时, 谁调用方法this就是谁。
  3. 当以构造函数的形势调用时, this就是新创建的对象。

输出

三种方式

通常可以有三种显示方法,即:以对话框的形式弹出信息、输出文本到浏览器显示和输出文本到浏览器的控制台。
以对话框的形式弹出信息我们使用 window.alert() 方法,输出文本到浏览器显示我们可以使用document.write()innerHTML 方法,输出文本到浏览器的控制台我们使用 console.log() 方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<html>
<head>
<script type="text/javascript">
//alert() 方法用于显示带有一条指定消息和一个确认按钮的警告框。
alert("警告消息");
//document.write() 方法可向文档写入 HTML 表达式或 JavaScript 代码。同时,一次可列出多个 参数(exp1,exp2,exp3,…) ,并将按顺序被追加到文档中。
document.write("文本1","文本2")
//console.log() 方法用于向控制台输出一条消息。支持 C 语言 printf 式的格式化输出。当然,也可以不使用格式化输出来达到同样的目的。
console.log("console");
function setInnerHTML(){
//innerHTML 属性设置或返回表格行的开始和结束标签之间的 HTML,tablerowObject.innerHTML=文本;
document.getElementById("test").innerHTML = "<strong>设置标签的html内容";//将id为test的标签内容替换为指定内容
}
</script>
</head><body>
<p id="test">原始内容</p>.
<input type="button" onclick="setInnerHTML()" value="点击" />
</body>
</html>

代码运行

alert

显示带有一条指定消息和一个确认按钮的警告框。对话框是一个模态窗口,它能阻止用户对浏览器窗口界面的其他部位进行操作。参数是要显示在对话框中的文本字符串,如果传入其他类型的值,会转换成字符串

write

在 JavaScript 中 document.write() 方法可向文档写入 HTML 表达式或 JavaScript 代码。同时,一次可列出多个参数(exp1,exp2,exp3,…) ,并将按顺序被追加到文档中。
虽然根据 DOM 标准,该方法只接受单个 字符串 作为 参数。不过根据经验,write() 可接受任何多个参数。
我们通常按照两种的方式使用 write() 方法:一是在使用该方法在文档中输出 HTML,另一种是在调用该方法的的窗口之外的窗口、框架中产生新文档。在第二种情况中,请务必使用 close() 方法来关闭文档。

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html>
<head>
<title>使用 JavaScript 的 document.write输出文本</title>
<script type="text/javascript">
console.log("嗨客网(www.haicoder.net)");
document.write("hello", 111,"<br>",222);
</script>
</head>
</html>

innerHTML

用于设置或返回表格行的开始和结束标签之间的 HTML。

console方法

方法用于向控制台输出一条消息。同时支持 C 语言 printf 式的格式化输出。当然,也可以不使用格式化输出来达到同样的目的。
console.log()系列方法

console.log 向控制台输出一条消息。
console.debug 向控制台输出一条信息,它包括一个指向该行代码位置的超链接。
console.info 向控制台输出一条信息,该信息包含一个表示“信息”的图标,和指向该行代码位置的超链接。
console.warn 同 info。区别是图标与样式不同。
console.error 同 info。区别是图标与样式不同。error 实际上和 throw new Error() 产生的效果相同,使用该语句时会向浏览器抛出一个 js 异常。
console.assert 断言,测试一条表达式是否为真,不为真时将抛出异常(断言失败)。
1
2
3
4
5
6
7
8
9
console.log("log消息")
console.debug("debug消息")
console.info("info消息")
console.warn("warm消息")
console.assert()//为空,返回failed
console.log("assert断言false,返回failed")
console.assert("不为空")
console.log("assert断言true则不作显示")
console.error("error消息")

代码运行

变量提升

在 JavaScript 中函数及变量的声明都将被提升到函数的最顶部。因此,在 JavaScript 中,变量可以在使用后声明,也就是变量可以先使用再声明。

变量提升的本质其实是由于 js 引擎在编译的时候,就将所有的变量声明了,因此在执行的时候,所有的变量都已经完成声明。

当有多个同名变量声明的时候,函数声明会覆盖其他的声明。如果有多个函数声明,则是由最后的一个函数声明覆盖之前所有的声明。

1
2
3
4
//先使用后声明
a = 100;
var a;
console.log(a)

代码运行
JavaScript 会将当前作用域的所有变量的声明提升到程序的顶部。如果修改如下,则会报错underfined,因为只有声明会被提升,赋值则不会。

1
2
3
console.log(a);
var a;
a = 100;

代码运行
当函数与变量同名时,函数的优先级更高

1
2
3
4
5
fun();
function fun() {
console.log("fun函数被执行")
}
var fun;

代码运行
可以看见函数被正常调用。如果是多个同名函数,则取后面的

1
2
3
4
5
6
function fun(){
console.log("fun1");
}
function fun(){
console.log("fun2");
}

代码运行
现在将代码修改如下:

1
2
3
4
5
fun;
var fun = funtion() {
console.log("函数被调用");
}
var fun;

代码运行
结果是“fun not a function”,这是因为”var fun = function(){}”是一个变量声明赋值语句,var fun也是,两个同名变量取后者,因此fun是undefined

var,let和const

var和let都是用于声明变量的关键字,但存在一定差异:
var 定义变量,没有块的概念,可以跨块访问,不能跨函数访问,不初始化出现undefined,不会报错。
let 定义变量,只能在块作用域里访问,也不能跨函数访问,对函数外部无影响。
const 定义常量,只能在块作用域里访问,也不能跨函数访问,使用时必须初始化(即必须赋值),而且不能修改。(这点有点特殊,后面讲)

var声明的变量作用域在整个函数范围内,或者全局:

1
2
3
4
5
6
function fun() {
if(true) {
var a = 1; //在if中声明变量
}
console.log(a) //if外访问变量,但仍在函数中
}

运行代码
结果是函数可以正常运行打印出a,说明var声明变量作用域是整个函数
如果换成let:

1
2
3
4
5
6
7
8
9
function fun() {
let a = "out";
if(true) {
let a = "in"; //在if中声明变量
console.log("if-a:",a);//访问if中的变量
}
console.log("fun-a:",a); //访问if外的变量
}
fun();

代码运行
const与let类似。
以及var可以声明相同变量,而let只能声明一次
最后关于const不能修改这点

1
2
3
const a = 10;
a = 100;
console.log(a)

当const声明的是一个简单类型时,是不能修改的,会报错。
但如果声明的是一个复杂类型(一般是对象和数组),则会出现不同的地方。

1
2
3
const a = [1,2,3];
a[0] = 0;
console.log(a);

没报错,数组被修改。这是为什么呢?
这是因为,const保证的其实是a指向的内存地址不改变,而不是该内存地址的内容不改变。简单类型其实就是常量,无法修改,想改变a的值只能重新指向新地址,但对于数组,他的值改变了,但地址并不会改变,也就不会引起a指向的改变。

严格模式

ECMAScript 5 的严格模式是采用具有限制性 JavaScript 变体的一种方式,从而使代码显示地脱离 “马虎模式/稀松模式/懒散模式“(sloppy)模式。

在 JavaScript 中,我们要开启严格模式,只需要使用 use strict 指令即可。
语法:

1
use strict;

放在代码顶部,也可放在函数前,只对函数生效,但一般是全局形式。它不是一条语句,是一个字面量表达式,在 JavaScript 旧版本中会被忽略。
为什么使用严格模式

  • 消除 JavaScript 语法的一些不合理、不严谨之处,减少一些怪异行为。
  • 消除代码运行的一些不安全之处,保证代码运行的安全。
  • 提高编译器效率,增加运行速度。
  • 为未来新版本的 JavaScript 做好铺垫。
  • “严格模式” 体现了 JavaScript 更合理、更安全、更严谨的发展方向,包括 IE 10 在内的主流浏览器,都已经支持它,许多大项目已经开始全面拥抱它。
  • 另一方面,同样的代码,在 “严格模式” 中,可能会有不一样的运行结果;一些在 “正常模式” 下可以运行的语句,在 “严格模式” 下将不能运行。掌握这些内容,有助于更细致深入地理解 JavaScript,让你变成一个更好的程序员。
  • 严格模式通过抛出错误来消除了一些原有静默错误。
  • 严格模式修复了一些导致 JavaScript 引擎难以执行优化的缺陷:有时候,相同的代码,严格模式可以比非严格模式下运行得更快。
  • 严格模式禁用了在 ECMAScript 的未来版本中可能会定义的一些语法。
    严格模式的限制
  • 不允许使用未声明的 变量。
  • 不允许删除变量或 对象。
  • 不允许删除 函数。
  • 不允许变量重名。
  • 不允许使用八进制。
  • 不允许使用 转义字符。
  • 不允许对只读属性赋值。
  • 不允许对一个使用 getter 方法读取的属性进行赋值。
  • 不允许删除一个不允许删除的属性。
  • 变量名不能使用 “eval” 字符串。
  • 变量名不能使用 “arguments” 字符串。
  • 由于一些安全原因,在作用域 eval() 创建的变量不能被调用。
  • 禁止 this 关键字指向全局对象。

运算符

其他懒得写,写不同的地方就好

比较运算符

不同点就是”===“和”!==“两个运算符,三等号表示等值等型,!==表示不等值或不等型

1
2
3
4
var num = 1;
var str = "1";
console.log("num === str :",num === str );//false
console.log("num !== str :",num !== str);//true

代码运行

三目运算符

js支持三目运算符,和三目嵌套

1
2
var retValue = a > b ? (a > c ? a : c)  : (b > c ? b : c);
//比较三数最大值

键盘输入

在 JavaScript 中,获取用户输入的数据使用 prompt 方法。

1
2
3
prompt(text, defaultText)
//text可选。提示信息,不是HTML格式。
//default可选。默认的输入文本。(一开始自动填入)

返回值:如果用户单击提示框的取消按钮,则返回 null。如果用户单击确认按钮,则返回输入字段当前显示的文本。

1
2
var a = prompt("输入名字:","张三");
alert(a);

代码运行
再用户输入时,会暂停对js代码的进行。

流程控制

if、elseif、while、for、switch等都与C语言一样,但js的for可以用for in语句,可以用于遍历 数组 的每一个元素或者用于遍历获取 对象 的每一个属性

1
2
3
for (i in boj){
code...
}

当使用 for in 循环遍历数组时,那么参数 i 是数组索引,而不是数组的元素。当使用 for in 循环遍历对象时,那么参数 variable 是对象的属性
break语句:js的break与C类似,但js可以指定中断的循环

1
2
3
4
5
6
7
loop:
for (var i = startIndex; i < endIndex; i++){
//do something
if (condition) {
break loop
}
}

这里loop是标签,后接“:”
continue也有一样的用法

函数

基本形式

JavaScript 的函数可以分为:自定义函数和系统函数。
JavaScript 的函数也支持普通函数、匿名函数闭包 三种形式。
js的函数声明不需要写返回值类型,定义参数也不需要声明类型。

1
2
3
function fun(a) {
return a;
}

函数参数默认值

如果我们在调用函数时没有输入参数,则使用我们定义的默认参数的值。函数的默认参数必须放在函数参数列表的最后,且默认参数可以是任意多个。

1
2
3
4
5
6
7
8
9
10
function fun1(name, year=20){
console.log(name);
console.log(year);
}
function fun2(name="张三", year=20){
console.log(name);
console.log(year);
}
fun1("lihua");
fun2();

代码运行

arguments

在 JavaScript 中,arguments 对象是比较特别的一个 对象,实际上是当前 函数 的一个内置属性。也就是说所有函数都内置了一个 arguments 对象。
arguments 对象中存储了传递的所有的 实参,arguments 是一个 伪数组(本质还是对象),因此可以进行遍历。
argument常用方法

函数 说明
arguments.callee arguments 所在的函数,指向当前执行的函数
arguments.caller 指向调用当前函数的函数
arguments.length 获得长度,实参的个数
arguments[i] 通过遍历索引,获得每个实参

函数变量

在js中函数也是一种数据类型,可以和其他类型一样,保存在变量中

1
2
3
4
5
function fun(){
console.log(123);
}
var f = fun();
f();

匿名函数

匿名函数就是不写函数名的函数,往往赋值给变量。匿名函数的调用方法有四种

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//赋值后调用
var f = funtion(){//赋值给变量
...
}
f();//调用函数

//小括号直接调用
function () {
...
}(实参列表);

//在事件中调用
let btn = document.getElementById("btn"); // 找到页面某个标签
btn.onclick = function(){ // 添加事件
console.info("你点了我!");
}

//通过对象调用
let myObj = { //声明对象
name : "HaiCoder",
sayHai:function(){
console.info("Hello,"+ this.name );
}
};
myObj.sayHai();

闭包

闭包就是一个 函数 和与其相关的引用环境组合的一个整体。
在 JavaScript 中闭包是引用了 自由变量 的函数,被引用的自由变量和函数一同存在,即使已经离开了自由变量的环境也不会被释放或者删除,在闭包中可以继续使用这个自由变量。
闭包(Closure)在某些编程语言中也被称为 Lambda 表达式
在 JavaScript 中,被捕获到闭包中的变量让闭包本身拥有了记忆效应,闭包中的逻辑可以修改闭包捕获的变量,变量会跟随闭包生命期一直存在,闭包本身就如同变量一样拥有了记忆功能。

1
2
3
4
5
6
7
8
9
10
11
function add(){
a = 20;
return function(x) {
a+=x;
return a;
}
}
var f = add();
console.log(f(1));//21
console.log(f(2));//23
console.log(f(3));//26

代码运行

作用域

作用域有全局、局部、和块级。任意一对花括号中的语句都属于一个块。

js词法规则

  • 函数允许访问函数外的数据
  • 整个代码结构中只有函数可以限定作用域
  • 作用域规则首先使用提升规则分析
  • 如果当前作用规则中有名字了, 就不考虑外面的名字

作用域链

只有函数可以制造作用域结构, 那么只要是代码,就至少有一个作用域,即全局作用域。凡是代码中有函数,那么这个函数就构成另一个作用域。如果函数中还有函数,那么在这个作用域中就又可以诞生一个作用域。

将这样的所有的作用域列出来,可以有一个结构: 函数内指向函数外的链式结构。就称作作用域链。

字符串函数

字符串长度

在 JavaScript 中要想获取字符串长度只需要使用字符串的 length 属性即可。JavaScript 获取字符串长度语法:

1
str.length;

截取字符串

在 JavaScript 中,截取字符串有三种方法,分别为:使用 substring 函数使用 substr 函数使用 slice 函数。

1
2
3
4
5
6
7
8
var str = "helloworld";
var str0_5 = str.substring(0,6);//提取两个0到5的字符,省略结尾则截取到结束
var str1_6 = str.substr(1,6);//从1位置开始,取长度为6的字符串。如果起始下标是负数则从尾部开始,长度省略则到结束
var str2_5 = str.slice(2,6);//提取2到6的字符,可以为负数,省略结尾则截取到结束

console.log("substring",str0_5);
console.log("substr",str1_6);
console.log("slice",str2_5);

代码运行
substring和slice的区别:

  1. slice接受负数,表示从结尾算起
  2. substring如果结束位置在起始位置之前,会自动调整,而参数小于0时按0处理

拼接字符串

JavaScript 拼接 字符串 有两种方法,分别是:使用 + 号拼接和使用 **join **函数 拼接。
加号直接加就好,join语法如下:

1
2
3
4
5
6
var arr  = new Array();//声明数组
var str1 = "Hello";
var str2 = "World";
arr.push(str1);//push方法将字符串存储数组中
arr.push(str2);
var str = arr.join("");//调用数组的join方法拼接,参数为分隔字符

代码运行

分割字符串

注意分隔和截取的区别
语法

1
stringObject.split(separator,howmany);

separator为必填,表示从该参数指定的地方开始分隔,可以填字符串或正则表达式。
howmany表示返回数组最大长度,如果不填则整个字符串都会被分隔
返回值:一个字符串数组,返回的数组中的字串不包括 separator 自身。
但是,如果 separator 是包含子表达式的 正则表达式,那么 返回 的数组中包括与这些子表达式匹配的字串(但不包括与整个正则表达式匹配的文本)。

说明:如果把“”空字符串做spearator做参数,则每个字符都被分隔

1
2
3
var str = "www.baidu.com";
var strs = str.split(".");
console.log(strs);

代码运行

查找子字符串

在一个字符串中查找另一个字符串有四种方法,分别为:使用 indexOf 函数、使用 lastIndexOf 函数、使用 match 函数和使用 search 函数。

indexOf

indexOf() 方法可 返回 某个指定的字符串在源字符串中首次出现的位置。如果没有则返回-1

1
stringObject.indexOf(searchvalue,fromindex)
参数 描述
searchvalue 必需。规定需检索的字符串值。
fromindex 可选的整数参数。规定在字符串中开始检索的位置。它的合法取值是 0 到 stringObject.length - 1。如省略该参数,则将从字符串的首字符开始检索。

lastIndexOf()

lastIndexOf() 方法可返回一个指定的字符串值最后出现的位置,在一个字符串中的指定位置从后向前搜索。如果找到一个 searchvalue,则返回 searchvalue 的第一个字符在 stringObject 中的位置。
用法同上

正则表达式匹配字符

在一个 字符串 中使用正则表达式去匹配查找另一个字符串有两种方法,分别为:使用 match 函数和使用 search 函数。

match()

match() 方法可在字符串内检索指定的值,或找到一个或多个正则表达式的匹配。该方法类似 indexOf() 和 lastIndexOf(),但是它 返回 指定的值,而不是字符串的位置。返回个数取决于是否启用全局检索

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var str = "hheelloohehehe";

//查找字符串
var str1 = str.match("he");

//使用正则表达式
var str2 = str.match(/he/);

//g全局模式
var str3 = str.match(/he/g);
console.log(str1);
//[ 'he', index: 1, input: 'hheelloohehehe', groups: undefined ]
console.log(str2);
//[ 'he', index: 1, input: 'hheelloohehehe', groups: undefined ]
console.log(str3);
//[ 'he', 'he', 'he', 'he' ]

代码运行

search()于检索字符串中指定的子字符串,或检索与正则表达式相匹配的子字符串。返回匹配子串的起始位置,没有则-1.
search不支持全局模式,但有忽略大小写模式‘i’

访问字符串字符

在 JavaScript 中要想获取 字符串 中某个下标的字符,有三种方法,分别为:使用 [] 加上下标访问、使用charAt函数 访问和使用 charCodeAt 函数访问。
charAt

1
str.charAt(index);

返回该索引处的字符,如果越界则返回undefined

charCodeAt

1
str.charCodeAt(index);

如果访问的索引在字符串中,则返回该索引处的字符的ACSII码,否则,返回NaN

字符串替换

在JavaScript中,在一个 字符串 中将特定的字符串替换为指定的字符串,可以使用 replace 函数。
replace() 函数用来查找匹配一个 正则表达式 的字符串,然后使用新字符串代替匹配的字符串。

1
stringObject.replace(regexp/substr,replacement)
参数 描述
regexp/substr 必需。规定子字符串或要替换的模式的 RegExp 对象。请注意,如果该值是一个字符串,则将它作为要检索的直接量文本模式,而不是首先被转换为 RegExp 对象。
replacement 必需。一个字符串值。规定了替换文本或生成替换文本的函数。

大小写转换

toLowerCase() 和 toUpperCase。

字符串连接

concat() 方法用于连接两个或多个数组或者字符串。该方法不会改变现有的数组,而仅仅会 返回 被连接数组的一个副本。

1
arrayObject.concat(arrayX,arrayX,......,arrayX)

返回值
返回一个新的数组。该数组是通过把所有 arrayX 参数添加到 arrayObject 中生成的。如果要进行 concat() 操作的参数是数组,那么添加的是数组中的元素,而不是数组。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var str1 = "hello";
var str2 = "world";
var str = str2.concat(str2);
console.log("str1 = ",str1);
console.log("str2 = ",str2);
console.log("str = ",str);

var arr1 = new Array(3);
arr1[0] = 1;
arr1[1] = 2;
arr1[2] = 3;
var arr2 = new Aray(3);
arr2[0] = 456;
arr2[1] = 789;
arr2[2] = 10;
var arr = arr1.concat(arr2);
console.log("arr1:",arr1);
console.log("arr2:",arr1);
console.log("arr:",arr);

代码运行

去除首尾空格

在 JavaScript 中去除 字符串 的首尾空格,使用** trim 函数。有些浏览器不支持** trim 函数,那么我们可以自己通过replace函数实现 trim 函数。有的浏览器不支持trim函数,我们可以用repalce函数实现相同的效果

1
2
3
function myTrim(x) {
return x.replace(/^\s+|\s+$/gm,'');
}

子串是否存在

可以使用字符串查找的方法,也可以使用includes方法,如果有则返回true,否则返回false。

1
str.includes(searchvalue, start);

参数为查找的子串和起始位置,位置可不写。

数组

JavaScript创建数组

JavaScript 的数组的创建有三种方法,即,使用 new 创建、使用 new 直接创建并实例化和隐式创建。
new

1
var arr = new Array();

new创建并实例化

1
var arr = new Array("1","2","3");

隐式创建

1
var arr = [];

隐式创建并初始化

1
var arr = [123];

初始化数组

除了上部分的创建并初始化,还有几种初始化方式fill初始化

1
var arr = new Array(count).fill(item);

可以将item元素填充满数组

1
2
var arr = new Array(10).fill(1);
console.log(arr);

代码运行

数组添加元素

向 JavaScript 的 数组 中添加元素有四种方法,即:直接使用 [] 添加元素、使用 push 添加元素、使用 unshift 添加元素和使用 splice 添加元素。

1
2
3
4
5
6
7
8
9
10
11
12
//直接使用下标添加,添加元素可以不紧跟末尾元素
var arr = Array(10);
arr[12] = 12;
console.log("arr1:",arr);

//向末尾添加一个或多个元素,并返回新长度
var len = arr.push(13,14,15);
console.log("arr2:",arr);

//向头部添加一个或多个元素,并返回新长度
var len2 = arr.unshift(0,1,2);
console.log("arr3:",arr);

代码运行

splic详解
JavaScript 的 splice() 方法向/从数组中添加/删除项目,然后返回被删除的项目

1
2
3
4
5
6
var arr = new Arrau(1,2,3,4,5)
//从下标0开始删除3个元素,并向数组被删除的位置添加新元素9,10
//个数写0表示不删除
var del = arr.splice(0,3,9,10)
console.log("del:",del)//1 2 3
console.log("arr:",arr)//9 10 4 5

数组删除元素

从 JavaScript 的 数组 中删除元素有七种方法,即:使用 length 属性删除、使用delete 删除、使用 pop 删除、使用 shift 删除、使用 splice 删除、使用迭代方式删除和使用 prototype 原型方法删除

length删除

js的长度是可以修改的

1
arr.length = arr.length - count;

delete

1
delete arr[index];

删除后长度不变,只是被赋值为undefined

pop

JavaScript 中的 Array 对象提供了一个 pop() 栈方法用于弹出并返回数组中的最后一项,某种程度上可以当做删除用。
栈数据结构的访问规则是 FILO(First In Last Out,先进后出),栈操作在栈顶添加项,从栈顶移除项,使用pop() 方法,它能移除数组中的最后一项并返回该项,并且数组的长度减 1

1
del = arr.pop();

shift

JavaScript 中的 Array 对象提供了一个 shift() 队列方法用于弹出并返回数组中的第一项,某种程度上也可以当做删除用。

队列数据结构的访问规则是 FIFO(First In First Out,先进先出),队列在列表的末端添加项,从列表的前端移除项,使用 shift() 方法,它能够移除数组中的第一个项并返回该项,并且数组的长度减 1。

1
del = arr.shift();

splic

在 JavaScript 的 Array 对象中提供了一个 splice() 方法用于对数组进行特定的操作。splice() 恐怕要算最强大的数组方法了,他的用法有很多种,在此只介绍删除数组元素的方法。

在删除数组元素的时候,它可以删除任意数量的项,只需要指定 2 个参数:要删除的第一项的位置和要删除的项数。

1
arrayObject.splice(index,howmany,item1,.....,itemX);

数组查找元素

使用 indexOf 方法、使用 lastIndexOf 方法、使用 findIndex 方法和使用 find 方法。前2者与string类似

findIndex

findIndex() 方法返回传入一个测试条件(函数)符合条件的数组第一个元素位置。
方法为元素调用函数(回调函数)

1
array.findIndex(function(currentValue, index, arr), thisValue);

作为参数的函数至少要有一个形参:数组元素,另外两个可选参数为下标和数组本身,当函数返回true时,findIndex返回下标,并结束。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/*
a为元素
b为下标
c为数组本身
*/
function f(a,b,c){
console.log("a = ",a);
console.log("b = ",b);
console.log("c[b] = ",c[b]);
return false;//一直false,便利整个数组
}
var arr1 = new Array(1,2,3);
var index = arr1.findIndex(f)
console.log(index);

function f2(a){//查找等于1的元素的下标
return a == 1;
}
var arr2 = new Array(0,2,1,1);
var index2 = arr2.findIndex(f2);
console.log(index2);

代码运行

在findIndex中还有一个可选参数,“thisValue”,传递给回调函数的值一般用 “this” 值。如果这个参数为空, “undefined” 会传递给 “this” 值。

1
2
3
4
5
6
7
8
function f(a){
console.log(this.age);
}
var me = {//创建me对象
age : "lh"
}
var arr = Array(3).fill(0);
var index = arr.findIndex(f,me);//将me对象传递给f函数中的this

代码运行

find

find和findIndex类似,但返回符合条件的第一个元素值,如果没有则返回undefined

数组元素是否存在

和string类似,使用includes方法

数组长度

使用length属性

遍历数组

JavaScript 的 数组 的遍历方法有七种,即:使用 for循环 遍历、使用 foreach 遍历、使用 for in 遍历、使用 for of 遍历、使用 keys方法遍历、使用 values 方法遍历和使用 entries 方法遍历。

foreach

forEach方法会每个元素调用一次函数(回调函数),使用方法与find类似,也有thisValue参数可选,返回索引

1
array.forEach(function(currentValue, index, arr), thisValue);

for of

用法类似for in,但for in返回的是下标,for of返回值

1
2
3
for (var value of arr){
...
}

map

JavaScript 的 数组 对象内置了 map 方法,可以用来遍历数组.用法类似forEach。为数组的每一个元素调用一个回调函数进行处理,处理完成后,将回调函数的返回值组成一个新的数组,若没有返回值则产生和原数组等长元素为undefined的数组

1
2
3
4
5
6
function f(a) {   //回调函数
return a*2;
}
var arr = new Array(1,2,3,4,5);
var newArr = arr.map(f,undefined);
console.log(newArr);

代码运行

filter

JavaScript 的 数组 对象内置了 filter 方法,可以用来遍历数组,为数组的每一个元素调用一个回调函数进行处理,如果数组的元素符合回调函数的要求,那么会将该元素添加到新数组中。用法同上

1
2
3
4
5
6
7
8
function f(a) {   //回调函数
if (a % 2 == 0){
return true;
}
}
var arr = new Array(1,2,3,4,5);
var newArr = arr.map(f,undefined);
console.log(newArr);

代码运行

every

用于检测数组中元素是否都符合条件,如果有一个不符合则返回false

1
2
3
4
5
6
7
8
function f(a) {   //回调函数
if (a > 0){
return true;
}
}
var arr = new Array(1,2,3,4,5);
var isPass = arr.every(f,undefined);
console.log(isPass);

代码运行

some

JavaScript 的 数组 对象内置了 some 方法用于检测数组中的元素是否都符合指定条件,如果只要一个元素符合指定条件,则返回 true,所有元素都不符合要求,则返回 false。
同上

reduce

JavaScript 的 数组 对象内置了 reduce 方法用于接收一个 函数 作为累加器,将数组中的每个值(从左到右)开始缩减,最终计算为一个值。

1
array.reduce(function(total, currentValue, currentIndex, arr), initialValue);
参数 描述
function(total,currentValue, index,arr) 必须。数组每个元素需要执行的函数。
initialValue 可选。传递给函数的初始值
参数 描述
total 必需。初始值, 或者计算结束后的返回值。
currentValue 必需。当前元素
index 可选。当前元素的索引
arr 可选。当前元素所属的数组对象
1
2
3
4
5
6
7
8
9
10
11
function allAdd(a,b,index){  //将所有值相加
console.log("index = ",index);
console.log("a = ",a);
console.log("b = ",b);
console.log("a+b = ",a+b);
console.log("------");
return a+b;
}
var arr = new Array(1,2,3,4,5);
var result = arr.reduce(allAdd);
console.log(result);

这样就是将整个数组相加,1+2+3+4+5。而initiaValue可以设置初始值

1
2
3
4
5
.
.
.
...arr.reduce(allAdd,100);//初始值为100
...

代码运行
这样就是100+(1+2+3+4+5)
如果回调函数没有返回值,则每次调用都会将NaN返回

reduceRight

和reduce用法一样,不过reduceRight是从尾部开始

keys和values

JavaScript 的 数组 的 keys() 方法用于从数组创建一个包含数组键的可迭代对象,其中 keys 返回 的迭代器数组的所有的索引,而不是数组的值。每个迭代器只能使用一次

1
2
3
4
5
var arr = new Array("q","w","e","r");
var keys = arr.keys();//获取keys迭代器
for (var key of keys) {//遍历迭代器,注意for in无法对迭代器使用
console.log(key);
}

JavaScript 的 数组 的 values() 方法用于从数组创建一个包含数组值的可迭代对象,其中 values 返回 的迭代器是数组的所有的值

1
2
3
4
var values = arr.values();//获取value迭代器
for (var value of keys) {//遍历迭代器
console.log(value);
}

代码运行

entire

JavaScript 的 数组 的 entries() 方法用于从数组创建一个包含数组键和值的可迭代对象,其中 entries 返回 的迭代器包含了数组的所有的键和值。

1
2
3
4
5
6
7
var arr = new Array("qe", "er", "Hello", "world");
console.log("Arr =", arr);
var entries = arr.entries();
console.log("entries =", entries);
for(var [key, value] of entries){ //接受键值对
console.log("Key =", key, "Value =", value);
}

代码运行

判断是否是数组

在 JavaScript 中,判断一个 变量 是否是 数组 使用 isArray 方法,如果是数组,则 isArray 返回 true,如果不是数组,则 返回 false。

1
Array.isArray(obj);
1
2
3
4
5
6
var a = [];
var b = 1;
var aIsArr = Array.isArray(a);
var bIsArr = Array.isArray(b);
console.log(aIsArr);
console.log(bIsArr);

数组合并

JavaScript 的 数组 的 concat() 方法用于将多个数组合并成一个数组,并 返回 合并后的数组。此方法并不会改变原来的数组,而仅仅是返回被连接的数组的一个副本。

1
array1.concat(arr2,arr3...);

数组转字符串

JavaScript 的 数组 的 toString() 方法 用于将数组转换为 字符串,同时,使用 toString() 方法将数组转成字符串时,数组的每个元素之间使用 “,” 分隔。

1
array.toString();

数组连接

JavaScript 的 数组 的 join() 方法用于将数组的所有元素连接成一个 字符串,同时,使用 join() 方法将数组连接成字符串时,还可以指定每个元素之间的分隔符。

数组拷贝

复制数组的一部分数据覆盖到数组的另一个位置。并返回数组,但是不会改变原数组的长度,会改变顺序返回拷贝过后的数组

1
array.copyWithin(target, start, end);
参数 描述
target 必需。复制到指定目标索引位置。
start 可选。元素复制的起始位置。
end 可选。停止复制的索引位置 (默认为 array.length)。如果为负值,表示倒数。
1
2
3
4
5
var arr = ["Hello", "Golang", "Hello", "JavaScript", "Hello", "Python"];
var arrRet = arr.copyWithin(0);
console.log("ArrRet =", arrRet);
var arrRet2 = arr.copyWithin(0, 2);
console.log("ArrRet2 =", arrRet2);

代码运行

数组填充fill

1
array.fill(value, start, end);

从字符串创建数组from

from() 方法用于通过拥有 length 属性的对象或可迭代的 对象 来 返回 一个数组

1
Array.from(object, mapFunction, thisValue);
参数 描述
object 必需,要转换为数组的对象。
mapFunction 可选,数组中每个元素要调用的函数,返回值存入数组
thisValue 可选,映射函数(mapFunction)中的 this 对象。
1
2
3
4
5
6
7
8
function mapFunction(a){//转换为数字后*2返回
var a_ = Number(a)*2;
return a_;
}
var str = "12345";
var newArr = Array.from(str,mapFunctionm,undefined);
var newArr2 = Array.from(str);//按字符类型创建数组
console.log(newArr2);

代码运行

字符串反转

在 JavaScript 中,我们可以使用 reverse 函数,实现将 数组 的元素进行反转,reverse 函数会 返回 反转后的数组

1
array.reverse();

数组排序

在 JavaScript 中,我们可以使用 sort 函数,实现对 数组 的元素进行排序,sort 函数会修改原来的数组

1
array.sort(sortfunction);

sortfunction指定排序顺序,必须是函数

1
2
3
4
5
6
7
8
9
10
11
12
13
var arr = [1024, 109, 38, 88, 110];
arr.sort();
console.log("arr =", arr);
function sortFunc(a, b){
return a - b;
}
function reverseSortFunc(a, b){
return b - a;
}
arr.sort(sortFunc);
console.log("arr =", arr);
arr.sort(reverseSortFunc);
console.log("arr =", arr);

面向对象

JavaScript对象特征

  1. 对象具有唯一标识性:即使完全相同的两个对象,也并非同一个对象。
  2. 对象有状态:对象具有状态,同一对象可能处于不同状态下。
  3. 对象具有行为:即对象的状态可能因为它的行为产生变迁。
  4. 在 JavaScript 中,将状态和行为统一抽象为 “属性”,考虑到 JavaScript 中将函数设计成一种特殊对象,所以 JavaScript 中的行为和状态都能用属性来抽象。

创建对象

在 JavaScript 中,创建一个 对象 有四种方法,即:通过 Object 方法创建、通过字面量方法创建、通过自定义函数(构造函数)创建 和 通过工厂模式创建
Object方法创建

1
2
3
4
5
6
7
8
var obj = new Object();
obj.attr1 = value1;
obj.attr2 = value2;
obj.attr3 = value3;
...
obj.func = function(){
//todo
}

字面量创建

1
2
3
4
5
6
7
8
var obj = {};
obj.attr1 = value1;
obj.attr2 = value2;
obj.attr3 = value3;
...
obj.func = function(){
//todo
}

自定义函数方法创建对象(类)

1
2
3
4
5
6
7
8
9
function Obj(attr1, attr2, attr3, ...){
this.attr1 = attr1;
this.attr2 = attr2;
this.attr3 = attr3;
...
this.func = function(){
//todo
}
}
1
2
3
4
5
6
7
8
9
10
11
function Person(name,age,sex){
this.Name = name;
this.Age = age;
this.Sex = sex;
this.Hi = function(){
console.log("my name is ",this.Name,", i am ",this.Age,"years old",",i am a ",this.Sex);
}
},

var xiaohon = new Person("小红",20,"girl");
xiaohon.Hi();

代码运行

对象属性

访问

使用.和[]访问对象属性

添加

访问后添加

删除

使用delete方法

1
delete person.name;

遍历对象属性

在 JavaScript 中,遍历一个 对象 的所有 属性 有五种方法,即:使用 for in 遍历、使用 Object.keys 遍历、使用 Object.getOwnPropertyNames 遍历、使用 - Object.getOwnPropertySymbols 遍历和使用 Reflect.ownKeys 遍历。

  • getOwnPropertyNames
    在 JavaScript 中,遍历一个 对象 的 属性 可以使用 getOwnPropertyNames() 方法,Object.getOwnPropertyNames() 方法会返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括 Symbol 值作为名称的属性,这是和keys的区别,keys只包括可枚举类型)组成的 数组。
    1
    Object.getOwnPropertyNames(obj);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
var sym = Symbol("symbol");
var obj = {
[sym] : 'symbol类型',
1 : 2,//1虽然是数字不可枚举类型,但做属性名会自动转字符串
/*
相当于:
'1' : 2,
*/
'2' : 3,
}
//返回非symbol的所有属性名
var key1 = Object.getOwnPropertyNames(obj);
console.log("key1=",key1);//['1', '2']
//返回非symbol的可枚举属性名
var key2 = Object.keys(obj);
console.log("key2=",key2);//['1', '2']
//设置属性不可枚举
Object.defineProperties(obj,{//一次定义多个属性,并设置它们的可枚举性。
1:{
value:2,
enumerable: false//将属性‘1’设置为不可枚举
}
})
//返回非symbol的可枚举属性名
var key3 = Object.keys(obj);
console.log("key3=",key3);//['2']
  • getOwnPropertySymbols
    在 JavaScript 中,遍历一个 对象 的 属性 可以使用 getOwnPropertySymbols() 方法,Object.getOwnPropertySymbols() 方法会返回一个 数组,该数组包含了对象自身的所有 Symbol 属性的键名
1
2
3
//只返回symbol类型属性名
var key4 = Object.getOwnPropertySymbols(obj);
console.log("key4=",key4);//[Symbol(symbol)]
  • ownKeys
    在 JavaScript 中,遍历一个 对象 的 属性 可以使用 ownKeys() 方法,Reflect.ownKeys 返回一个 数组,该数组包含对象自身的所有键名不管键名是 Symbol 或 字符串,也不管是否可枚举。
    1
    Reflect.ownKeys(obj);
1
2
3
//返回所有属性名
var key5 = Reflect.ownKeys(obj);
console.log("key5=",key5);//'1', '2', Symbol(symbol)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
var sym = Symbol("symbol");
var obj = {
[sym] : 'symbol类型',
1 : 2,//1虽然是数字不可枚举类型,但做属性名会自动转字符串
/*
相当于:
'1' : 2,
*/
'2' : 3,

//返回非symbol的所有属性名
var key1 = Object.getOwnPropertyNames(obj);
console.log("key1=",key1);//['1', '2']
//返回非symbol的可枚举属性名
var key2 = Object.keys(obj);
console.log("key2=",key2);//['1', '2']
//设置属性不可枚举
Object.defineProperties(obj,{//一次定义多个属性,并设置它们的可枚举性。
1:{
value:2,
enumerable: false//将属性‘1’设置为不可枚举
}
})
//返回非symbol的可枚举属性名
var key3 = Object.keys(obj);
console.log("key3=",key3);//['2']
//只返回symbol类型属性名
var key4 = Object.getOwnPropertySymbols(obj);
console.log("key4=",key4);//[Symbol(symbol
//返回所有属性名
var key5 = Reflect.ownKeys(obj);
console.log("key5=",key5);//['1', '2', Symbol(symbol)]

代码运行

对象存在

在 JavaScript 中,判断一个 对象 是否有某个 属性 有三种方法,即:使用点或者中括号运算符、使用 in 语句和使用 hasOwnProperty 方法。
访问到不存在属性时,会返回undefined

in判断

1
attr in obj;

如果对象 obj 中,含有属性 attr,那么 in 语句返回 true,否则,返回 false。

hasOwnProperty

1
obj.hasOwnProperty(attr);

hasOwnProperty 方法返回一个布尔值,判断对象是否包含特定的自身(非继承)属性(与in的区别),如果存在,则返回 true,否则,返回 false。

对象方法

在 JavaScript 中,方法是能够在 对象 上执行的动作。JavaScript 方法是包含函数定义的 属性

this

面向对象语言中 this 表示当前 对象 的一个引用。但在 JavaScript 中 this 不是固定不变的,它会随着执行环境的改变而改变。
JavaScript this特点详解

  1. 函数在定义的时候 this 是不确定的,只有在调用的时候才可以确定。
  2. 一般函数直接执行,内部 this 指向全局 window,在严格模式下,this 是 undefined
  3. 函数作为一个对象的方法,被该对象所调用,那么 this 指向的是该对象
  4. 构造函数中的 this 其实是一个隐式对象,类似一个初始化的模型,所有方法和属性都挂载到了这个隐式对象身上,后续通过 new 关键字来调用,从而实现实例化。

new

在 JavaScript 中,new 运算符用于创建一个用户定义的 对象 类型的实例或具有构造函数的内置对象的实例。

instanceof

在 JavaScript 中,我们判断某个 变量 是什么 类型 的常常会用 typeof 运算符,但是使用 typeof 运算符不管引用的是什么类型的对象,它都返回的是 object 类型的。而instanceof 运算符可以查看具体的类型

1
option instanceof constructor;
参数 描述
option 是需要检测的对象。
constructor 是你确定的对象,返回的是这个 option 是否属于 constructor 这个类型的布尔值。

构造函数

在 JavaScript 中,用 new 关键字来调用的 函数,称为构造函数
在使用对象字面量创建一系列同一类型的对象时,这些对象可能具有一些相似的特征(属性)和行为(方法),此时会产生很多重复的代码,而使用构造函数就可以实现代码的复用。
JavaScript构造函数执行流程

  1. 当以 new 关键字调用时,会创建一个新的内存空间,标记为对象的实例。
  2. 函数体内部的 this 指向该内存。
  3. 执行函数体内的代码。
  4. 默认返回 this

值类型和引用类型

在 JavaScript 中 变量 的类型可以分为基本类型(也叫值类型)与复杂类型(也叫引用类型),其中,值类型就是 JavaScript 的基本数据类型,引用类型是复杂的对象类型。
值:字符串(string)、数值(number)、布尔值(boolean)、null、undefined。
引用类型:对象(Object)、数组(Array)、函数(Function)、日期(Date)、正则表达式(RegExp)。

值类型 引用类型
概念 原始值指的是原始类型的值,也叫基本类型 引用值指的是引用类型(类) 的值
数据类型 字符串(string)、数值(number)、布尔值(boolean)、null、undefined。 对象(Object)、数组(Array)、函数(Function)、日期(Date)、正则表达式(RegExp)。
存储 栈(stack),占内存空间固定,使用后被销毁 堆(heap),占内存空间不固定,使用后不一定被销毁,只有一个对象没有任何引用时,系统的垃圾回收机制才会回收销毁
赋值方式 1. 值的拷贝,创建一个新对象
2. 保存与复制的是值本身
3. 两份数据在内存中是完全独立的
1. 引用的拷贝,创建一个新引用
2. 保存与复制的是指向对象的一个指针
3. 变量中的存储的地址赋值一份单独存储,
两个变量中修改其中一个对象,另外一个引用来访问的时候,也会访问到修改后的值。
4. 使用 new() 方法构造出的对象是引用型。
作用域 函数作用域,在函数内部修改时生效,函数销毁时失效 函数中被修改时修改的是运行时数据区中的值,即使函数被销毁,变量的值依旧被改变。
比较方式 值的比较 引用的比较
参数传递 值类型按值传递 引用类型按引用传递
检测类型 typeof 运算符 instanceof 运算符在函数内部修改时生效,函数销毁时失效

原型

每一个 对象 (除 null 外)创建的时候,就会与之关联另一个对象,这个对象就是我们所说的原型,每一个对象都会从原型中 “继承”属性
prototype
在 JavaScript 中,每个 函数 都有一个 prototype 属性,这个属性指向函数的原型对象。
proto
每个对象(除 null 外)都会有的属性,叫做 __proto__,这个属性会指向该对象的原型。
每个原型都有一个 constructor 属性,指向该关联的构造函数

原型链

在 JavaScript 中,每个继承父函数的子函数的对象都包含一个内部属性 _proto_。该属性包含一个指针,指向父函数的 prototype。

若父函数的原型对象的 proto 属性为再上一层函数,在此过程中就形成了原型链。

继承

JavaScript 中,实现继承有六种方法,即,分别为:使用原型链继承、借用构造函数继承原型链+借用构造函数的组合继承、组合继承组合继承–优化版 以及 ES6 中 class 的继承。

原型链继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 定义父类
function Parent() {
thia.name = 'parent'; // name属性
}

// 定义子类
function Child() {
this.name;
}
// 在父类的原型上定义getName方法
Parent.prototype.getName = function(){
return this.name;
}

// 让子类继承父类
Child.prototype = new Parent();

// 实例化子类
var c1 = new Child();

console.log(c1.getName()); // 输出'child'

我们通过将 Child的 prototype 设置为了 Parent,从而实现了继承。

原型链继承的主要问题是如果属性是引用类型,这个属性会被所有实例共享,如果修改了一个,其他实例的属性也会被修改,比如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 定义父类
function Parent() {
thia.array = [1,2,3]; // name属性
}

// 定义子类
function Child() {}

// 继承
Child.prototype = new Parent();

// 实例化子类
var c1 = new Child();
var c2 = new Child();

// 测试
c1.array; //[1,2,3]
c2.array; //[1,2,3]
c1.array[0] = 99;
c1.array; //[99,2,3]
c2.array; //[99,2,3]

构造函数继承

通过使用callapply方法,可以在子类中执行父类型构造函数,从而实现继承。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 父类
function Parent(name, age) {
this.name = name;
this.age = age;
this.sayHello = function () {
console.log('hello');
}
}
// 在父类的原型上定义属性
Parent.prototype.a = "我是父类prototype上的属性";
// 子类
function Child(name, age, gender) {
// 使用call继承父类的属性
Parent.call(this, name, age);
this.gender = gender;
}

// 测试
var c1 = new Child('c1','1','boy');
var c2 = new Child('c2','1,','boy');

console.log(c1.sayHello == c2.sayHello;) // false,说明两个函数(引用类型)存储在不同的地址上
console.log(c1.a); // undefined, 说明这种继承方式无法继承原型链上的属性

构造函数继承无法继承原型链上的属性,只能继承父类构造函数内的属性和方法,优点是所有属性和方法,无论是否是引用类型,继承都是独立的

原型链和构造函数组合继承详解

组合继承将两种方式一起使用使得子类的原型属性(即父类构造函数内的属性)不会被共享,且可以继承父类原型链上的属性和方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 父类
function Parent(name, age) {
this.name = name;
this.age = age;
this.array = [1,2,3]
}
// 父类原型添加属性
Parent.prototype.a = 1;
// 子类
function Child(name, age, gender) {
Parent.call(this,name,age);
this.gender = gender;
}
// 原型链继承
Child.prototype = new Parent();
// 修改子类构造函数指向
Child.prototype.constructor = Child; //组合继承也是需要修复构造函数指向的

// 测试
var c1 = new Child(1,1,1);
var c2 = new Child(2,2,2);
c1.array === c2.array; // false,说明地址不同,不会共享
console.log(c1.a); // 1,可以访继承父类原型链上的属性

缺点:调用了两次Parent,在Child.prototype上添加了Parent的属性和方法

寄生组合继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 父类
function Parent(name, age) {
this.name = name;
this.age = age;
this.array = [1,2,3]
}
// 子类
function Child(name, age, gender) {
// 使用call继承父类的属性
Parent.call(this,name,age);
this.gender = gender;
}

// create函数将创建一个空对象并指定该对象的原型
// 通过这个方法实现对父类原型链的继承
Child.prototype = Object.create(Parent.prototype);
// 修复构造函数指向
Child.prototype.constructor = Parent;

寄生组合继承只调用了一次Parent()函数,但是**Child.prototype的原始属性和方法会丢失**

class 的继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 父类
class Parent {
//调用类的构造方法
constructor(attr1, attr2) {
this.attr1 = attr1;
this.attr2 = attr2;
}
}
// 子类继承父类
class Child extends Parent {
constructor(attr1, attr2, attr3) {
super(attr1, attr2); //通过super调用父类的构造方法
this.attr3 = attr3;
}
}

Proxy对象

Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 包装的目标对象
const target = {
value: '1'
}

// 一个通常以函数为属性的对象,定义了在执行各种操作时代理 p 的行为
const handler = {
get: function (target, property) {
console.log(target, property); // {value: '1'} '1'
return target[property];
}
}
// 设置代理
const p = new Proxy(target, handler);
// 使用代理读取属性,并触发get方法
console.log(p.value);
// {value: '1'} '1' get方法打印的值
// ‘1’ get方法返回的值被console.log打印

下面是handle对象的方法

方法名 作用
handler.getPrototypeOf() Object.getPrototypeOf 方法的捕捉器,当读取代理对象的原型时,该方法就会被调用
handler.setPrototypeOf() Object.setPrototypeOf 方法的捕捉器,主要用来拦截 Object.setPrototypeOf()
handler.isExtensible() Object.isExtensible 方法的捕捉器,用于拦截对对象的 Object.isExtensible()。
handler.preventExtensions() Object.preventExtensions 方法的捕捉器,主要用来拦截 Object.preventExtension()
handler.getOwnPropertyDescriptor() Object.getOwnPropertyDescriptor 方法的捕捉器,方法是 Object.getOwnPropertyDescriptor()的钩子。
handler.defineProperty() Object.defineProperty 方法的捕捉器,用于拦截对象的 Object.defineProperty()操作。
handler.has() in 操作符的捕捉器
handler.get() 属性读取操作的捕捉器
handler.set() 属性设置操作的捕捉器
handler.deleteProperty() delete 操作符的捕捉器
handler.ownKeys() Object.getOwnPropertyNames 和 Object.getOwnPropertySymbols 方法的捕捉器
handler.apply() 函数调用操作的捕捉器
handler.construct() new 操作符的捕捉器

map映射

map和对象有点类似,都是以键值对的形式存储数据,他的使用方式如下:

1
2
3
4
5
6
7
8
9
10
11
12
// 声明一个map
var myMap = new Map();
// 使用set存入数据
myMap.set("key", "value");
// 使用get读取数据
console.log(myMap.get('key')); // '1'
// 使用delete删除数据
myMap.delete('key'); // 根据删除成功或失败返回true或false
// 使用clear清空map
myMap.clear();
// size属性查看map的大小
myMap.size;

接下来是对象和map的区别:

  • 键的类型:

    对象的键只能是字符串或symbol类型,而map接受所有类型,比如数组、数字、函数等

  • 键的顺序:

    在对象中,键的顺序并不是按顺序的,比如数字键会被提前,而map的键则是按照输入顺序

  • 性能:

    对象的属性查找性能通常在处理大量数据时较差,尤其是当键是非字符串类型时,浏览器和 JavaScript 引擎可能会执行额外的类型转换。

  • 序列化:

    对象可以直接与 JSON 进行序列化和反序列化。JSON.stringify()JSON.parse() 支持对象的转换。Map 不能直接与 JSON 序列化工具一起使用。如果需要序列化 Map,需要先将其转换为数组。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 对象序列化与反序列化
    const obj = { a: 1, b: 2 };
    const json = JSON.stringify(obj); // '{"a":1,"b":2}'
    const parsed = JSON.parse(json); // { a: 1, b: 2 }

    // map间接序列化
    const map = new Map([['a', 1], ['b', 2]]);
    const json = JSON.stringify([...map]); // '[["a",1],["b",2]]'
    const parsed = new Map(JSON.parse(json)); // Map { 'a' => 1, 'b' => 2 }