基本要求:

window.onload:先加载,最后执行:

浏览器默认是从上往下执行代码的

因为我的js代码一般都要写到最后,script写在body的最后面,但是我想写在head的话,我就需要使用window.onload给他包裹起来,当所有的加载都完毕的时候,执行包裹的代码。

这个和vue3中的生命周期钩子,onMounted类似,都是加载完成后在进行。

对于弹窗:

1
2
console.log()//用于调试
alert()//用于浏览器弹框

网页内容输入:

document.write()

常量和变量:

ES6新增:const和let,其中const为常量,let为变量

而在之前还有var,现在不用了。

  1. 可以先使用在声明,存在变量提升,仅仅是不用声明了,但是依旧是没有赋值的,此时打印仍为undefined,只是不报错罢了。
  2. 没有块级作用域,要么是全局要么是整个函数(不会被if、for{}给包起来)

对于const、let,同时变量声明是有顺序的,后面的值会把前面的给覆盖掉。

对于const为常量的理解:不可变的是指针内存地址,而不是对象内部的数据

例如const arr = [],你可以通过数组的push方法进行修改里面的内容,但是不能让arr = [1]指向一个新的数组

基本数据类型:

引用数据类型只有一种,那就是Object

  • 对象属性: 你可以随意给它添加 name, age 等属性。
  • 动态性: 因为是值传递,可以在运行时随时增删改内容。
  • 共享性: 多个变量可以指向同一个内存空间,修改的只是值。

栈内存和堆内存:

栈内存是对于数据进行操作,而我的堆内存是对于内存地址进行操作,速度慢一些。

字符串:string

转义字符:

  • \' 表示 ' 单引号
  • \" 表示 " 双引号
  • \\ 表示\
  • \r 表示回车
  • \n 表示换行。n 的意思是 newline。
  • \t 表示缩进。t 的意思是 tab,制表符。
  • \b 表示空格。b 的意思是 blank。

这些的作用就是,因为都是字符串类型,需要使用引号,会与我的引号重复,所以说需要使用特殊的字符来代替我所想要表达的意思。

字符串拼接:

用**+(更加偏向隐式转化为字符串类型),而别的运算符更加偏向转为number**

当使用拼接的时候,会自动把数据类型转换为字符串类型。

隐式转换:

-*/%这几个符号会自动进行隐式转换,是将字符串类型自动转换为数字类型。(记住没有+)

对于+来说,字符串的拼接是最优先的。

模板字符串:(很常用)

1
2
3
4
5
var name = 'qianguyihao';
var age = '26';

console.log('我是' + name + ',age:' + age); //传统写法
console.log(`我是${name},age:${age}`); //ES6 写法。注意语法格式,在括号里面是反引号``

数值型:Number:

  1. 不分家: JS 不分 int, float, double,统一都是 Number(底层是 64 位浮点数)。
  2. 有陷阱: 小数运算不精准(如 0.1 + 0.2 != 0.3),因为二进制存储时会产生舍入误差(计算机都是二进制,就像十进制1/3=0.33333…..一样,二进制也无法处理小数,会产生误差)。
  3. 避坑指南: > 当涉及小数计算,先乘 10 的倍数化为整数,算完再除回去。例如:(0.1× 10 + 0.2 × 10) / 10 = 0.3
    1. 或者使用 toFixed(n) 保留几位小数(注意:返回的是字符串)。

连字符和加号的区别:

如果+两边都是数字,那么就是加号,否则就是连字符。

对于Undefined和null:

更常用的是Underfined,null只是一个用来定义空对象的。

在进行计算的时候,underfine和任何进行计算都是NaN,而null可以当成0来计算。

数据类型的转化:

显式类型转换:

  • toString()
  • String()
  • Number()
  • parseInt(string)
  • parseFloat(string)
  • Boolean()

隐式类型转换:

  • isNaN() 函数
  • 自增/自减运算符:++—-
  • 运算符:正号+a、负号-a
  • 运算符:加号+
  • 运算符:-*/%
  • 比较运算符:<><=>===等。比较运算符的运算结果都是布尔值:要么是 true,要么是 false。
  • 逻辑运算符:&&||! 。非布尔值进行与或运算时,会先将其转换为布尔值,然后再运算。&&||的运算结果是原值,!的运算结果为布尔值。

隐式类型转换实际上还是调用显示转换的方法。

转换为String:

  1. 调用tostring方法:

该方法不会影响到原变量,但是对于null和undefined是无法转换的,会报错。

1
2
let a1 = 123;
console.log(a1.tostring);

对于进制来说(二进制等),只是表现形式,但是对于计算机来说,所有的表现形式实质上都是字符串。

  1. 调用string方法:

它和tostring方法不一样的就是,更加安全,即就是对于null和undefined来说,不会报错。

对于其他本质上就是在调用tostring,而对于特殊的:将 undefined 直接转换为 “undefined”,将 null 直接转换为 “null”。

转换为Number:

使用number()函数

原始值 转换后的值
字符串 (1)字符串去掉首尾空格后,剩余字符串的内容如果是纯数字,则直接将其转换为数字。 (2)字符串去掉首尾空格后,剩余字符串包的内容只要含了其他非数字的内容(小数点按数字来算),则转换为 NaN。怎么理解这里的 NaN 呢?可以这样理解,使用 Number() 函数之后,如果无法转换为数字,就会转换为 NaN。 (3)如果字符串是一个空串或者是一个全是空格的字符串,则转换为 0。
布尔值 true 转成 1;false 转成 0
undefined NaN
null 0

parseInt:

  • parseInt()/parseFloat() :提取出最前面的数字部分(开头如果是空格,则自动忽略空格);没提取出来,那就返回 NaN。

注意不是四舍五入保留整数,而是直接截断整数,例如5.8直接变成5

有包含进制转化的功能:

1
2
3
4
5
var a = '110';

var num = parseInt(a, 16); // 【重要】将 a 当成 十六进制 来看待,转换成 十进制 的 num

console.log(num);

隐式转换类型:

  1. boolean + 数字 = 数字

boolean 型和数字型相加时, true 按 1 来算 ,false 按 0 来算。这里其实是先调 Number() 函数,将 Boolean 类型转为 Number 类型,然后再和 数字相加。

  1. null + 数字 = 数字

等价于:0 + 数字

  1. undefined + 数字 = NaN

计算结果:NaN

  1. 任何值和 NaN 运算的结果都是 NaN。

转换为布尔:

空串和全是空格的字符串含义不同:

其中空串为:’’ “”

而全是空格的字符串:’ ‘

就是这个意思

使用 !!

使用 !!可以显式转换为 Boolean 类型。比如 !!3的结果是 true。

运算符:

幂运算:

ES6新增语法,**即就是:

1
2
2**3;
Math.pow(2, 3);

浮点数:

  1. 不要直接比较两个浮点数的大小,肯定不对
  2. 对于涉及金钱的数据处理交给后端
  3. 因为计算机表示都是用二进制来表示的,无法精确表示浮点数,修改规则的话成本会非常高。

自加运算符:

对于这种运算符来说,不能对常量和表达式使用,只能操作变量。

typeof:

1
2
typeof 数字(含 typeof NaN)//结果为number
对于[]和{}来说,他们都是空对象,返回结果为object

非布尔值的与或运算【重要】:

1
2
3
4
5
6
7
//&&它就是且的意思,全真才能为真
a&&b:如果第一个为真,返回第二个值
但是如果第一个为假,那么返回的就是第一个。

//||他是或的意思,只要有一个为真,返回结果就是真的
a||b 如果第一个为假,那么返回第二个值。
如果第一个为真,直接返回第一个值。
  • 对于三元运算符:其中&&就是从前往后找第一个fasle,如果找不到的话就返回最后一个。
    • 对于||从前往后找第一个true,如果找不到就返回最后一个。

===和==区别:

==会做隐式类型转换

===则不做类型转换。

‘6’和6,是不等的,一个是字符串,一个是数字本身。

单目运算符和双目运算符:

就比如+来说:

单目:只有一个操作符,i++

双目:多个,a+b

break和continue:

break语句:

  • 可以用来退出switch语句或者整个循环语句(while、for循环)
  • break会立即终止离他最近的循环语句
  • label的使用:可以指定跳转循环
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const matrix = [
[1, 2, 3],
[4, 5, 6], // 假设我们要找 5
[7, 8, 9]
];
// 定义一个标签名,通常大写或驼峰,紧跟冒号
outerLoop:
for (let i = 0; i < matrix.length; i++) {
console.log(`正在检查第 ${i} 行...`);

for (let j = 0; j < matrix[i].length; j++) {
console.log(` 检查数值: ${matrix[i][j]}`);

if (matrix[i][j] === 5) {
console.log('✅ 找到了!停止所有循环。');
// 直接跳出名为 outerLoop 的外层循环
break outerLoop;
}
}
}

console.log('循环已彻底结束');

continue语句:

break是退出所有循环,而continue是退出当前循环

其余的性质都是通用的。

循环来说:

打印99乘法表,在输出的时候第二个循环参数在前,如果需要整齐,需要设置一个span标签,并且给其设置inline-block属性,如果需要在块级元素中居中,text-align:center;因为不同数字大小是不同的,所以说可以统一规格,每个块的大小是一样的,完全能容纳乘法元素

其中block是独占一行,不管内容有多少都是独占一行

但是inline-block,是排满一行才会换行。

内置对象:

创建对象:let obj1 = new Object();

属性和方法只能添加给对象,不能添加给基本数据类型。

基本包装类型:

在底层是以字符串数组的形式保存的,因此既可以获得字符串长度,也可以根据index索引获取单个位置的元素,就像操作数组一样。

1
2
3
let str = 'smyhvae';
console.log(str.length); // 获取字符串的长度
console.log(str[2]); // 获取字符串中的第3个字符(下标为2的字符)

string字符串的常见方法:

  • 查找字符串:indexOf()从前往后遍历
    • lastIndexOf()从后往前遍历
    • 查询结果为-1说明没找到
1
2
3
4
5
6
7
8
9
10
11
12
索引值 = str.indexOf(想要查询的字符串, [起始位置]);


let paragraph = "小明喜欢看书,小明也喜欢写代码,小明还喜欢运动。";
let target = "小明";
let pos = paragraph.indexOf(target); // 找到第一个小明

while (pos !== -1) {
console.log(`在位置 ${pos} 发现了一个小明`);
// 关键点:从当前位置 + 1 的地方继续往后找,否则会陷入死循环
pos = paragraph.indexOf(target, pos + 1);
}

endsWith和startsWith:

这俩的起始索引不一样

1
2
3
4
5
6
7
// 因为指定了起始位置为3,所以是在 defg 这个字符串中检索。
console.log(name.startsWith('d',3)); // 打印结果:true
console.log(name.startsWith('c',3)); // 打印结果:false

// 因为指定了截止位置为3,所以是在 abc 这个长度为3字符串中检索(实际上是前三个索引0,1,2)
console.log(name.endsWith('c', 3)); // 打印结果:true
console.log(name.endsWith('d', 3)); // 打印结果:false

获取指定位置字符串:(str.通过.操作符进行访问)

  1. charAt(index) (老代码的维护)
  2. str[index](最常用)
  3. charCodeAt(index)
    1. 返回指定字符串的Unicode编码,不会修改原字符串。(a是97,A是65)

字符串截取:

  1. slice()方法:(支持负数)

  • (2, 5) 截取时,包左不包右。
  • (2) 表示从指定的索引位置开始,截取到最后。
  • (-3) 表示从倒数第三个开始,截取到最后。
  • (1, -1) 表示从第一个截取到倒数第一个。(对于hello来说,截取的是ell)

特殊在于如果前面的值大于后面,直接返回空字符串。

  1. substring()

其余和slice方法一样

特殊的就是,不能接受负值,如果传递的是负值,默认使用0。

如果第二个参数小于第一个参数,比如说, substring(1, 0)相当于截取的是第一个字符。(注意substring会把所有的负数当作NaN处理,也就不存在substring(1, -2)这种交换方法)

和slice不同,可以自作聪明,如果后面的数字大于前面的话,会进行自动交换。

split():字符串转换为数组(很重要)

通过指定分隔符,将字符串拆分成一个数组。不会改变原字符串。

split()无参数,就是将一整个字符串添加到数组中。

split(‘’),把每一个字符添加到数组中

其余就是在’’加指定字符进行分割。

注意:在切割的时候,如果是切割的是两个数字中间的数,会返回两个值,但是呢,如果右边没有值的话,我会返回一个空字符串。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 例子 1:标准分割
let names = "张三,李四,王五";
console.log(names.split(",")); // ["张三", "李四", "王五"]

// 例子 2:你提到的边界情况(右边没值)
let str = "1,2,";
let result = str.split(",");
console.log(result); // ["1", "2", ""]
// 看!逗号右边虽然没东西,但它依然占了一个“空字符串”的位置。

// 例子 3:无参数 vs 空字符串参数
let word = "Hi";
console.log(word.split()); // ["Hi"] (整个塞进去)
console.log(word.split('')); // ["H", "i"] (每个字符都拆开)

replace():

(返回的是一个新的字符串,旧的不需要接受) = str.replace(被替换的子串,新的子串);

只能替换第一个字符串,如果要替换所有的字符串,需要使用正则表达式

1
2
3
4
5
6
7
8
let str = "苹果红了,苹果甜了,苹果熟了";

// 我想把“苹果”都换成“大西瓜”
let newStr = str.replace("苹果", "大西瓜");

console.log(newStr);
// 结果是:"大西瓜红了,苹果甜了,苹果熟了"
// 只有第一个变了,后面的“苹果”依然稳如泰山。

正则表达式:

1
2
3
4
5
let str = "apple, apple, apple";
// 把 "apple" 放在两个斜杠中间,后面加个 g
let newStr = str.replace(/apple/g, "banana");

console.log(newStr); // "banana, banana, banana"

replaceAll();(比正则表达式简单一些)

1
2
3
4
let str = "apple, apple, apple";
let newStr = str.replaceAll("apple", "banana"); // 顾名思义,全部替换

console.log(newStr); // "banana, banana, banana"

repeat():

重复字符串,可以用来模糊处理电话号码的后四位

1
2
3
4
5
const telephone = '13088889999';
const mix_telephone = telephone.slice(0, -4) + '*'.repeat(4); // 模糊电话号码的后四位

console.log(telephone); // 打印结果:13088889999
console.log(mix_telephone); // 打印结果:1308888****

大小写转换:

1
2
3
4
5
6
let str = 'abcdEFG';
//转换成小写
console.log(str.toLowerCase());

//转换成大写
console.log(str.toUpperCase());

几个疑惑点解答:

1
2
3
let a = {}//定义的是一个对象
let a = []//才定义是一个数组
for(let k in a)//是遍历对象的方法

内置数学函数:

1
2
3
4
5
6
7
8
9
10
Math.abs()//绝对值,可以对传进来的字符串进行隐式转换
Math.random()//生成[0,1)的随机数

如下图:生成 [x, y]之间的随机整数

function getRandom(min,max){
return Math.floor(Math.random()*(max-min+1)+min)
}

console.log(getRandom(1,10))

Date()对象:

一定要注意,月份和日期是从0开始的

方法名 含义 备注
getFullYear() 获取年份
getMonth() 获取月: 0-11 0代表一月
getDate() 获取日:1-31 获取的是几号
getDay() 获取星期:0-6 0代表周日,1代表周一
getHours() 获取小时:0-23
getMinutes() 获取分钟:0-59
getSeconds() 获取秒:0-59
getMilliseconds() 获取毫秒 1s = 1000ms
1
2
3
4
5
6
7
8
9
10
const now = new Date();

//制作星期几的中文显示
const day = now.getDay(); // 获取星期:0-6
const weekArr = ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"];
// 💡应用:0代表周日,正好对应数组索引0
console.log("今天是:" + weekArr[day]);

//获取月份要加1
const month = now.getMonth() + 1;

数组:

数组解构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let [a, b, c] = [1, 2, [3, 4]];
console.log(a); // 1
console.log(b); // 2
console.log(c); // [3, 4]

//在进行赋值之前,可以给左边的变量设定默认值
let [a, b = 3, c = 4] = [1, 2];
console.log(a); // 1
console.log(b); // 2。默认值被覆盖。
console.log(c); // 4。继续保持默认值。

//使用拓展运算符打包剩余数据:
let [a, ...b] = [1, 2, 3];
console.log(a); // 1
console.log(b); // [2, 3]

数组元素的删除和添加:

push():向数组的最后插入一个或多个元素,返回结果为新数组的长度。会改变原数组,因为原数组变成了新数组。(返回的是新长度)

1
2
3
4
5
6
7
8
9
10
11
var arr = ['王一', '王二', '王三'];

var result1 = arr.push('王四'); // 末尾插入一个元素
var result2 = arr.push('王五', '王六'); // 末尾插入多个元素

//JSON.stringify()是将数组转换为字符串进行打印
console.log(JSON.stringify(arr)); // 打印结果:["王一","王二","王三","王四","王五","王六"]

//注意打印结果,返回的不是数组,而是数组的长度
console.log(result1); // 打印结果:4
console.log(result2); // 打印结果:6

pop():删除数组中的最后一个元素,返回结果为被删除的元素。(返回的是被弹出删除的数字)

1
2
3
4
5
var arr = ['王一', '王二', '王三'];
var result1 = arr.pop();

console.log(JSON.stringify(arr)); // 打印结果:["王一","王二"]
console.log(result1); // 打印结果:王三

将伪数组转换为真数组:

Array.from(lis,callback),第二个参数是回调函数

const result = Array.from(lis, item => Number(item.innerText)).reverse();

  1. 什么是伪数组:有索引值0,1,可以获取长度length,但是不能调用有关数组的方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function sum() {
// 这里的 arguments 是一个伪数组,其arguments本身是函数内部自带的,包含了所有传进来的参数。
console.log(arguments instanceof Array); // false (它不是真数组)

// ❌报错示范:
// arguments.push(10); // 会报错!因为伪数组没这方法

// ✅ 【核心操作】转换为真数组
const realArray = Array.from(arguments);

console.log(realArray instanceof Array); // true (变身成功!)

// 既然成了真数组,就可以用强大的 API 了
// 比如:使用 reduce 进行求和
return realArray.reduce((prev, curr) => prev + curr, 0);
}

console.log(sum(1, 2, 3)); // 结果:6
  1. 还有网页dom元素也是也是伪数组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 获取的是伪数组 NodeList
const lis = document.querySelectorAll('li');

// ❌ 错误做法:
// lis.pop(); // 报错!伪数组不能弹出元素

// ✅ 正确做法:利用 Array.from 转换
const liArray = Array.from(lis);

// 现在可以尽情使用数组 API 了
liArray.forEach(li => {
li.innerText += '★';
});

// 甚至可以用 pop() 移除最后一个元素的显示
const lastOne = liArray.pop();
lastOne.style.color = 'red';

indexOf() 和 lastIndexOf():获取元素的索引:

indexOf()在检索时,是严格类型约束,类似于===

也就是说不会进行隐式转换

1
2
let arr = ['1', '2', '3', '4', '5'];
console.log(arr.indexOf(2));//结果是-1,即找不到

forEach(item,index,arr):

回调函数中传递三个参数:

  • 参数1:当前正在遍历的元素
  • 参数2:当前正在遍历的元素的索引
  • 参数3:正在遍历的数组

数组是引用数据类型,存储的是地址,与基本数据类型不一样,如果你直接修改对应值的话,地址不变,那么你是无法修改的,但是你可以通过.操作符对对象中的某一项进行修改,修改的是地址值,数值会发生改变。

如果我想修改数组中的值,那么就要进行index索引进行赋值

1.

1
2
3
4
5
6
7
8
9
10
11
12
//在原有的数据上进行操作
const number = [1, 2, 3]

number.forEach((item, index, arr) => {
arr[index] = arr[index] * 3
})

console.log(number);

//也可以用map来运算(返回的是一个新值)
let newNumber = number.map(item => item * 3)
console.log(newNumber);

img

1.

1
2
3
4
5
6
7
8
9
10
11
const object = [
{ name: '锚链走', age: '19' },
{ name: 'woain', age: '90' }
]
object.forEach((item, index, arr) => {
arr[index] = {
name: '小明',
age: '89'
}
})
console.log(object)

img

1.

1
2
3
4
5
6
7
8
9
const obj = [
{ name: '锚链走', age: '19' },
{ name: 'woain', age: '90' }
]

obj.forEach((item, index, arr) => {
arr[index].name = '我是小白'
})
console.log(obj);

img

map集合:

是可以返回一个新的数组,可以对返回的值进行链式调用。

  • map一定要写return(但是箭头函数可以简写在一行的时候就可以省略这个return),或者用一个新的参数来接收,如果没有return那么就返回undefined。
1
2
3
4
5
6
7
8
const salary = [4000, 7000, 3000, 5000];

const result = salary
.map(item => item * 1.1) // 第一步:涨薪,返回新数组 [4400, 7700, 3300, 5500]
.filter(item => item > 5000) // 第二步:过滤,返回新数组 [7700, 5500]
.sort((a, b) => b - a); // 第三步:降序排列,返回 [7700, 5500]

console.log(result); // [7700, 5500]

filter:

返回的是布尔值,从数组中筛选出来满足条件的值进行返回

return语句也就代表着选择的结束,要注意循环的位置,如果是在if-else语句中,那么就可以起到筛选的作用,满足条件的return出去,不满足条件的接着执行代码。

Splice:

使用splice删除数组的时候,因为js是紧密数组,当我删除一个元素的时候,其余元素会进行补位,如果不进行补位的话,那么删除的元素会变成undefined,那么我在删除数组的时候可能会漏掉一些。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
let arr = [1, 2, 2, 3];

for (let i = 0; i < arr.length; i++) {
if (arr[i] === 2) {
arr.splice(i, 1); // 发现 2,删除它
}
}
console.log(arr); // 结果竟然是 [1, 2, 3]!漏掉了一个 2

//解决办法一:
if (arr[i] === 2) {
arr.splice(i, 1); // 发现 2,删除它
i--//删除的时候元素进行补位的同时我的索引值再往后
}

//解决办法二:
for (let i = arr.length - 1; i >= 0; i--) {
if (arr[i] === 2) {
arr.splice(i, 1); // 删除了也不怕,前面的元素索引没变
}
}

函数:

形参和实参:

形式参数:不是真的参数,只是代表我的接受我传递的实际参数。

实参:就是我的传值

注意他们的个数问题,最好是要一样。

如果实参个数小,没有影响,即就是我的多余实参没有传值进入罢了。

但是一旦形参少了,没有接收到的值会变成undefined,那么在运算的时候结果会变成NaN。

对于…操作符:

  1. 在形参里叫“剩余参数”:负责打包。把散装的参数收纳成一个真数组
1
2
3
console.log(...[1, 2, 3])

相当于 console.log(1, 2, 3)。
  1. 在实参里叫“展开运算符”:负责拆散。把数组拆开成散装的参数。

优势:彻底取代 arguments

  • 支持箭头函数。
  • 原生就是真数组,直接链式调用 map/filter/reduce
1
2
3
4
5
6
7
8
9
10
11
function sum(...args) {
...
}
把传进来的 1, 2, 3 打包成 [1, 2, 3]。

//剩余参数...args取代了arguments伪数组
function demo(...args) {
console.log(args instanceof Array); // true
// 它直接可以点出所有 API,不需要 Array.from()
return args.map(item => item * 2);
}

立即执行函数:

1
2
3
4
5
6
7
8
9
var arr = [];
for (var i = 0; i < 5; i++) {
arr.push(function () {
console.log(i);
});
}
arr[2](); // 打印5
//var声明没有块级作用域,而是全局作用域,整个循环只有一个i,因为push的是函数方法,log打印还没有执行,i就从0变化到5了。
//而我使用let就拥有了局部作用域,每次创建一个新的i值。
  • 当我使用立即执行函数
1
2
3
4
5
6
7
8
9
var arr = [];
for (var i = 0; i < 5; i++) {
(function (j) {
arr.push(function () {
console.log(j);
});
})(i);
}
arr[2](); // 打印2

this指向:

其中call和apply是立即执行的,bind需要调用才能执行

call():

call() 方法的作用:可以调用一个函数,与此同时,它还可以改变这个函数内部的 this 指向。

call() 方法的另一个应用:可以实现继承。之所以能实现继承,其实是利用了上面的作用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//语法
fn1.call(想要将this指向哪里, 函数实参1, 函数实参2);

//继承举例
// 给 Father 增加 name 和 age 属性
function Father(myName, myAge) {
this.name = myName;
this.age = myAge;
}
function Son(myName, myAge) {
// 通过这一步,将 father 里面的 this 修改为 Son 里面的 this;
Father.call(this, myName, myAge);
}
//new会创建一个新的空对象
const son1 = new Son('千古壹号', 28);
console.log(JSON.stringify(son1));

apply() 方法

需要传递数组,apply是一个拆箱的过程,是将数组拆成一个一个的数字

1
2
3
4
5
6
7
8
9
10
11
12
13
//语法
fn1.apply(想要将this指向哪里, [函数实参1, 函数实参2]);

//这样的目的就是,Math.max方法只认数字,不认数组,通过这样的方法可以对数组进行求值
const arr1 = [3, 7, 10, 8];
// 下面这一行代码的目的,无需改变 this 指向,所以:第一个参数填 null,或者填 Math,或者填 this 都可以。严格模式中,不让填null。
const maxValue = Math.max.apply(Math, arr1); // 求数组 arr1 中元素的最大值
//等价于:这样不就传递的是数组吗?
const maxValue = Math.max.apply(Math,[3, 7, 10, 8] );
console.log(maxValue);

const minValue = Math.min.apply(Math, arr1); // 求数组 arr1 中元素的最小值
console.log(minValue);

bind() 方法

不会立即执行,返回的是一个函数,我必须要调用才行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const person = {
name: "张三",
sayHi: function() {
//this指向person对象

const innerFunc = function() {
console.log("你好,我是" + this.name);
};

//如果不修改this指向,我在执行setTimeout的时候,this会指向全局对象window
const boundFunc = innerFunc.bind(this);
setTimeout(boundFunc, 1000);
}
};

person.sayHi();

闭包:(内部函数+所引用的外部变量)

闭包是为了解决全局变量数据,会被修改,进行数据的私有化。(执行完变量并不会被释放,所以会造成内存泄露,可以手动释放赋值null即可)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function createCheckTemp(standardTemp) {
// 形参 n 表示具体学生的分数
return function checkTemp(n) {
if (n >= standardTemp) {
alert('成绩合格');
} else {
alert('成绩不合格');
}
}

// 创建一个 checkStandard_A 函数,它以60分为合格线
var checkStandard_A = createCheckTemp(60);
// 再创建一个 checkStandard_B 函数,它以70分为合格线
var checkStandard_B = createCheckTemp(70);

// 调用函数
checkStandard_A(65); // 成绩合格
checkStandard_B(65); // 成绩不合格

深拷贝和浅拷贝:

浅拷贝:(只赋值第一层属性,如果是基本数据类型,是值传递,如果是引用数据类型就是址传递,即就是内存地址)

Object.assgin() 方法用的最多:

1
2
3
4
5
// 语法1
obj2 = Object.assgin(obj2, obj1);

// 语法2
Object.assign(目标对象, 源对象1, 源对象2...);
  • 其中浅拷贝的方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const myObj = {
name: 'qianguyihao',
age: 28,
};

// 【写法1】浅拷贝:把 myObj 拷贝给 obj1(会修改原有的obj1)
const obj1 = {};
Object.assign(obj1, myObj);

// 【写法2】浅拷贝:把 myObj 拷贝给 obj2(直接创建一个新的对象)
const obj2 = Object.assign({}, myObj);

// 【写法3】浅拷贝:把 myObj 拷贝给 obj31。注意,这里的 obj31 和 obj32 其实是等价的,他们指向了同一个内存地址
const obj31 = {};
const obj32 = Object.assign(obj31, myObj);

很重要!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const obj1 = {
name: 'qianguyihao',
age: 28,
desc: 'hello world',
};

const obj2 = {
name: '许嵩',
sex: '男',
};

// 浅拷贝:把 obj1 赋值给 obj2。这一行,是关键代码。这行代码的返回值也是 obj2
Object.assign(obj2, obj1);

console.log(JSON.stringify(obj2));

深拷贝:

structuredClone(obj2, obj1);

在深拷贝中,如果我修改obj2的值,不会影响obj1里面的值。

  • 总结:

遇简单类型则“赋值”:因为基本数据类型在内存中是按值传递的,改一个不会影响另一个。

遇复杂类型则“开辟新空间并递归”:这是为了打破“引用关系”。而复杂数据类型是按照址传递的。

jQurey:

  • 其中$是一个函数,和jQuery是等价的(为了解决不同浏览器的获取元素方法不同的问题)

img

let,var,const:

  • let和const是ES6新增的
  • var是一个全局变量,而let和const是一个块级变量,主要体现在for循环中(解决办法可以用立即执行函数和let块级作用域声明)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!DOCTYPE html>
<html lang="">
<head>
<meta />
<meta />
<meta />
<title>Document</title>
</head>
<body>
<input type="button" value="aa" />
<input type="button" value="bb" />
<input type="button" value="cc" />
<input type="button" value="dd" />
<script>
var myBtn = document.getElementsByTagName('input');

for (var i = 0; i < myBtn.length; i++) {
myBtn[i].onclick = function () {
alert(i);
};
}
</script>
</body>
</html>

暂时性死区:

对于let和const,强制先声明后使用。

解构赋值:

let [a, b, c] = [1, 2, 3];

对于解构赋值,如果在左边都有默认值的情况下,赋值undefined,仍为默认值,而赋值null,那么就会null

1
2
3
4
5
6
7
8
9
10
{
let [a, b = 'qianguyihao'] = ['千古壹号', undefined];
//b 虽然被赋值为 undefined,但是 b 会采用默认值
console.log(a + ',' + b); //输出结果:千古壹号,qianguyihao
}
{
let [a, b = 'qianguyihao'] = ['千古壹号', null];
//b 被赋值为 null
console.log(a + ',' + b); //输出结果:千古壹号,null
}

…操作符:

  1. 剩余参数:是用来接收的,剩余元素放到一个数组中的
  2. 扩展运算符:浅拷贝(保证第一层数据会指向一个新的内存地址),不随原来数据的改变而改变。
  • 将数组拆成一个个的字符串。

如果我想在一个对象中放入另一个对象,直接操作是不行的,这个时候我就需要用…操作符进行解构然后赋值,即就是:

1
2
3
4
5
6
7
8
9
10
11
12
const baseInfo = { name: '张三', age: 25 };
const workInfo = { job: '前端开发', salary: '15k' };

// 使用 ... 展开运算符
const user = {
...baseInfo,
...workInfo,
city: '北京' // 还可以顺便加个新属性
};

console.log(user);
// 输出: { name: '张三', age: 25, job: '前端开发', salary: '15k', city: '北京' }