JavaScript 知识点
1. 作用域
- 全局作用域
- 【函数外】,在全局作用域定义的变量
- 全局变量可以在任何地方使用
- 局部作用域
- 【函数内】,在局部作用域定义变量
- 局部变量只能在当前作用域使用
- 块级作用域(ES6)
- 【大括号内】,在块级作用域定义的变量
- 块级变量只能在当前块级使用
2. 声明变量关键字
-
var
、let
、const
(常量) -
var
和let
区别:var
声明的比那辆属于window,let
声明的变量不属于windowvar
声明的变量不具有块级作用域,let
声明的变量具有块级作用域var
可以重复声明,let
不可以重复声明
-
conts
(常量)const
声明的变量不属于windowconst
声明的变量具有块级作用域const
不可以重复声明const
声明的变量必须初始化有值const
声明的变量不允许改值
- 建议
- 声明常量时,常量名全大写
- 不变的值建议用常量定义
3. 作用域链
-
作用域链:由作用域串联起来链
-
作用:提供查找变量的机制
-
当一个作用域访问一个变量时,先从当前作用域查找,如果不存在这个变量,那么就会往上级查找
4. 闭包
- 闭包
- 一个作用域有权访问另外一个作用域的局部变量
- 子函数访问了父函数的局部变量,然后把子函数返回到外面
- 作用
- 延伸变量的使用范围,避免变量污染
// fn 为闭包函数
function fn () {
let num = 666
return function fun () {
console.log( num )
}
}
let re = fn()
re() // 666
5. 预解析
- js运行三部曲
- 先语法分析 扫面一遍看看有没有语法错误
- 预解析
- 解释执行 解释一行执行一行
- 预解析四部曲
- 创建AO对象
- 找形参和变量声明,将变量和形参名作为AO属性名,值为
undefined
- 将实参值和形参值统一
- 在函数里面找函数声明,值赋予函数体
- 预解析:代码执行之前先要预解析
- 变量:带有声明的变量,也叫变量的提升
- 把变量的声明语法提升到当前作用域的最前面
- 注意:只声明不复制
- 函数:带有名字的函数
- 把函数的声明语法提升到当前作用域的最前面
- 注意:只声明不调用
- 预解析
- 函数声明整体提升 – 声明变量提升 - 变量不赋值
- 变量 声明提升 – 声明函数提成 - 函数不执行
- 变量:带有声明的变量,也叫变量的提升
let
生命前不可以用,生命后才可以使用,const
同let
function test(a, b){
console.log(a); // function a () {}
console.log(b); // undefined
var b = 234;
console.log(b); // 234
a = 123;
console.log(a); // 123
function a () {}
var a;
b = 234;
var b = function () {}
console.log(a); // 123
console.log(b); // function () {}
}
6. 函数
6.1 参数默认值
// 求任意两个数的和
function getSum ( a = 0, b = 0 ) {
// let a = 0
// let b = 0
let sum = a + b
console.log( sum )
}
getSum( 1 ) // 1 + 0 = 1
getSum() // 0 + 0 = 0
getSum( undefined, 6 ) // 0 + 6 = 6
6.2 动态参数 - arguments
- 遇到参数不固定的情况:
arguments
- arguments: 函数内部的特殊对象,用于接收所有参数
- 注意:
arguments
是一个伪数组
- 建议:
- 如果参数不固定用
arguments
, 但是如果参数固定的情况下直接用形参
- 如果参数不固定用
function fn2 () {
// console.log( arguments ) // arguments = [1, 2, 3, 'a', 'aa']
for ( let i = 0; i < arguments.length; i++ ) {
console.log( arguments[i] )
}
}
// 实参的个数不确定
fn2( 1, 2, 3, 'a', 'aa' ) // 1 2 3 a aa
// 求若干个数的最大值
function getMax () {
let max = arguments[0]
for ( let i = 1; i < arguments.length; i++ ) {
if ( max < arguments[i] ) max = arguments[i]
}
console.log( max )
}
getMax( 123, 5, 62, 81, 21, 564, 551, 621, 22 )
6.3 ...
- 剩余参数、扩展运算符
6.3.1 剩余参数 - ...
-
ES6
-
接收剩余参数
...
-
只能放到形参的最后一位
function fn1(a, b, ...c) {
console.log(a, b, c) // 刘备 关羽 ['张飞', '黄忠', '马超']
}
fn1("刘备", "关羽", "张飞", "黄忠", "马超")
function fn2(...a) {
console.log(a) // 刘备 关羽 ['张飞', '黄忠', '马超']
}
fn2("刘备", "关羽", "张飞", "黄忠", "马超") // ['刘备', '关羽', '张飞', '黄忠', '马超']
// 求若干数的和
function getSum(...ary) {
// console.log( ary ) // [3, 6, 4, 84, 84, 89, 5, 12]
let sum = 0
for (let i = 0; i < ary.length; i++) {
sum += ary[i]
}
console.log(sum)
}
// 任意个数的实参
getSum(3, 6, 4, 84, 84, 89, 5, 12) // 287
6.3.2 扩展运算符 - ...
let arr = [23, 25, 69, 84, 92, 62]
// console.log(Math.max(arr)) // 报错 Math.max(这里需要一堆数而不是一个数组)
console.log(...arr) // 23 25 69 84 92 62
let re2 = Math.max(...arr)
console.log(re2) // 92
6.4 箭头函数 - () => {}
- 箭头函数
let fn = () => {}
let fn = ( a, b ) => {return a * b}
let fn = a => {return a * a}
let fn = a => a * a
// 1.箭头函数基本用法
let fn1 = (a, b) => {
console.log(a, b)
}
fn1(11, 22) // 11 22
// 2.箭头函数其他形式
// 2.1 如果函数的参数只有一个,那么可以省略小括号
let fn2 = (a) => {
return a * a
}
console.log(fn2(3)) // 9
// 2.2 如果函数只有一行代码,那么可以省略大括号
// 注意:如果省略了大括号,那么会默认返回结果
let fn3 = (a) => a * a
console.log(fn3(6))
// 3. 箭头函数使用场景:
// 3.1 间歇定时器
;setInterval(() => {
console.log("aaa")
}, 1000);
// 3.2 forEach遍历数组
[1, 2, 3].forEach((item) => console.log(item))
-
箭头函数注意事项
- 在箭头函数中不存在arguments,所以箭头函数不可以使用arguments
let fn1 = ( ...a ) => { // console.log( arguments ) // 报错 console.log( a ) // [1, 5, 6, 8, 651, 5] } fn1( 1, 5, 6, 8, 651, 5 )
- 箭头函数不存在预解析,所以是用箭头函数的时候必须先声明后调用
let fn2 = () => { console.log( 123 ) } fn2()
this
: 指向问题
- 箭头函数中,
this
指向的是上级作用域的this
(指向所在作用域的this
) - 建议:有
this
不建议使用箭头函数
let obj = { uname : '张三丰', fei : function () { console.log( this ) // Object setInterval( function () { console.log( this ) // window },1000 ) setInterval( () => { console.log( this ) // Object },1000 ) } } obj.fei()
- 箭头函数可以有 事件对象
document.documentElement.addEventListener('click', e => { console.log( e.target ) // html })
- 箭头函数 this 指向例子
document.documentElement.addEventListener('click', function (e) { console.log( this ) // html }) document.documentElement.addEventListener('click', e => { console.log( this ) // window })
6.5 函数的注意事项
-
函数如果有
return
, 会把结果返回 -
如果函数只写
return
不写值,那么默认返回一个undefined -
如果函数不写
return
,在函数执行完成之后也会返回undefined
7. 解构赋值
- 作用
- 解开数据结构,赋给变量 == 赋值给变量的一种简介写法
7.1 数组解构 - 核心:一一对应
let [a, b, c, d, e] = ['刘备', '关羽', '张飞', '黄忠', '马超']
console.log( a, b, c, d, e ) // 刘备 关羽 张飞 黄忠 马超
// 1.1 变量少值多
let [a1, b1, c1] = ['刘备', '关羽', '张飞', '黄忠', '马超']
console.log( a1, b1, c1 ) // 刘备 关羽 张飞
// 1.2 变量多值少
let [a2, b2, c2, d2, e2, f2, g2] = ['刘备', '关羽', '张飞', '黄忠', '马超']
console.log( a2, b2, c2, d2, e2, f2, g2 ) // 刘备 关羽 张飞 黄忠 马超 undefined undefined
// 1.3 按需取值
let [, a3, , , b3] = ['刘备', '关羽', '张飞', '黄忠', '马超']
console.log( a3, b3 ) // 关羽 马超
// 1.4 剩余值取法
let [a4, b4, c4, ...d4] = ['刘备', '关羽', '张飞', '黄忠', '马超']
console.log( a4, b4, c4, d4 ) // 刘备 关羽 张飞 ['黄忠', '马超']
// 1.5 多维结构
let [, a5, , [b5, , c5]] = ['刘备', '关羽', '张飞', ['孙悟空', '猪八戒', '沙悟净']]
console.log( a5, b5, c5 ) // 关羽 孙悟空 沙悟净
7.2 对象解构 - 核心:把属性名当作变量名
let uname = '哇哈哈'
// 注意:如果已经存在某个变量名,用冒号改名字
let { uname:userName, score, age, agee } = {
uname : '张三丰',
age : 22,
sex : '男',
inedx : 6,
score : 99
}
// 注意:取的对象属性里没有,输出就是undefined
console.log( userName, score, age, agee ) // 张三丰 99 22 undefined
// 2.1 多维结构
let {car : {width, height}} = {
windth : '150CM',
windth : '150CM',
car : {
color : 'red',
width : '550CM',
height : '850CM',
}
}
console.log( width, height ) // 550CM 850CM
8. 对象-构造函数
8.1 创建对象
8.1.1 字面量创建对象
// 1. 字面量创建对象
let obj = {
// 属性 : 属性值
// 键 : 值
// 成员
uname : 'HiHi',
sex : '男',
age : 22,
// 方法
say : () => console.log('我说了一句话'),
bai : () => console.log('拜拜')
}
// 1.1 访问属性:
// 语法1:对象.属性 // 固定属性访问
console.log( obj.uname ) // HiHi
// 语法2:对象['属性名'] // 动态属性访问
console.log( obj['age'] ) // 22
// 1.2 调用方法:
// 语法:对象.方法()
// console.log( obj.say ) // () => console.log('hello')
console.log( obj.say() ) // hello
- 注意:
- 对象里的成员是无序的
8.1.2 构造函数创建对象
- 构造函数:构造函数也是函数,只不过这个函数和new一起使用
- Object:
- 实例化对象: 就是 new 出来的对象
// 创建举例1:
let o = new Object()
// 添加成员
o.uname = 'Hi-Z' // 没有就添加
o.uname = 'Pi-M' // 有就修改
o.eat = () => console.log('吃饭中...')
o.say = () => console.log('说话中...')
// 创建举例2:
let o1 = new Object( {uname : 'haha', age : 22, sex : '男'} )
console.log( o1 )
- 注意
- 如果不加参数,那么Object构造函数后面的小括号可以省略
- 构造函数建议首字母大写
8.1.3 自定义构造函数
- 构造函数里的this指向当前实例对象
function Person ( uname, age, sex ) {
this.uname = uname
this.age = age
this.sex = sex
this.say = () => {
console.log( 'say方法' )
}
this.eat = () => {
console.log( 'eat方法' )
}
}
// 实例化对象
let o1 = new Person( '阿飞', 18, '男' )
console.log( o1 ) // Person {uname: '阿飞', age: 18, sex: '男', say: ƒ, eat: ƒ}
let o2 = new Person( '假阿', 18, '女' )
console.log( o2 ); // Person {uname: '假阿', age: 18, sex: '女', say: ƒ, eat: ƒ}
8.2 遍历对象/遍历数组 - for in
// 遍历对象 for in
for ( let key in obj ) {
// key: 代表对象里所有键
// console.log( obj.key ) // 固定属性写法 都是undefined
console.log( `${key}:${obj[key]}` ) // 动态属性写法
}
// 遍历数组 for in
let arr = [ 1, 3, 5, 7, 9 ]
for ( let i in arr ) {
console.log( arr[i] )
}
8.3 构造函数-this
指向
- 事件处理函数里的this指向事件源
- 箭头函数里的this指向父级的this指向
- 构造函数里的this指向的是当前实例对象(new 出来的对象叫实例对象)
8.4 new
的执行
- 执行过程
- 开辟空间
- this 指向这个函数
- 执行函数
- 返回这个对象
// 3. 执行函数
function Person ( uname, age ) {
this.uname = uname
this.age = age
this.say = () => console.log('哇哈哈')
}
// 4.返回这个函数
// 1.开辟空间 2.this指向这个函数
let p1 = new Person( '张飞', 18 )
console.log( p1 )
8.5 构造函数-功能关键字
instanceof: 检测对象是否是某个构造函数的
constructor: 用来指回构造函数本身
function A () {}
function B () {}
// instanceof: 检测对象是否是某个构造函数的
// 语法:对象 instanceof 构造函数
let n = new B()
console.log( n instanceof A )
console.log( n instanceof B )
// constructor: 用来指回构造函数本身
// 语法:对象.constructor
console.log( n.constructor )
8.6 构造函数-成员
-
静态成员:
-
直接在函数身上的成员,称为静态成员
-
只有构造函数才可以使用
-
-
实例成员:
-
函数内部给实例对象准备的成员,称为实例成员
-
只有实例对象才可以使用
-
function Person (uname, age) {
// 实例成员 (下三行)
this.nuame = uname
this.age = age
this.say = function () {
console.log('说话')
}
}
// 实例化对象(p1)
let p1 = new Person('张飞', 22)
console.log( p1 )
// console.log( Person instanceof Object ) // true
// 静态成员 (下两行)
Person.language = '汉语'
Person.eat = function () {
console.log('吃饭')
}
8.7 基本类型和引用类型的传递
- 传递:
- 值传递:把数据复制一份,传递 = 两份数据
- 引用传递:把数据地址复制一份,传递 = 一份数据
let n = 1
let m = n // 基础类型传递的是值 复制值并传递
n = 2
console.log( n, m ) // 2 1
let o = {uname : '阿飞', age : 18}
let obj = o // 复杂类型传递的是地址 复制地址并传递
o.uname = '阿灰' // 因为地址相同,一变多变
console.log( o, obj ) // {uname: '阿灰', age: 18} {uname: '阿灰', age: 18}
9. 内置构造函数
引用类型:
Object
Array
RegExp
包装类型:
String
Number
Boolean
9.1 Object
构造函数
Object
构造函数的两个方法:
Object.keys
: 获取对象所有键
Object.values
: 获取对象所有值
Object
: 是内置的一个构造函数
// 创建对象
// 字面量:{键值对}
let obj = {}
// 构造函数:Object
let o = new Object()
// 字面量创建obj1对象
let obj1 = {
uname : '张三丰',
age : 20,
say : function () {
console.log('say方法')
}
}
// 静态方法
// Object.keys: 获取对象所有键
let re1 = Object.keys( obj1 )
console.log( re1 ) // ['uname', 'age', 'say']
// Object.values: 获取对象所有值
let re2 = Object.values( obj1 )
console.log( re2 ) // ['张三丰', 20, ƒ]
9.2 Array
构造函数-方法
// 字面量:
let a = [1, 2, 3]
// Array: 构造函数,用于创建数组的构造函数
let arr = new Array('a', 'b', 'c')
console.log( a, arr) // [1, 2, 3] ['a', 'b', 'c']
- 字面量创建数组,也就相当于
new
了一个实例对象
9.2.1 reverse
- 反转
reverse
: 反转数组顺序
-
注意:会改变元数组
let arr1 = ['a', 'b', 'c', 'd', 'e'] let re1 = arr1.reverse() console.log( re1 ) // ['e', 'd', 'c', 'b', 'a'] console.log( arr1 ) // ['e', 'd', 'c', 'b', 'a']
9.2.2 join
- 拼接单元
join: 拼接数组的每个元素,拼接成为字符串
let arr2 = ['a', 'b', 'c', 1, 2, 3]
let re2 = arr2.join('') // 引号里可以填数组单元值之间用什么隔开
console.log( re2 ) // abc123
console.log( arr2 ) // ['a', 'b', 'c', 1, 2, 3]
9.2.3 concat
- 拼接数组
concat
: 把多个数组或者元素拼接成数组
let a1 = [1, 2, 3]
let b1 = ['a', 'b', 'c']
let c1 = [11, 22, 33]
let arr3 = a1.concat( b1, c1, '哇哈哈' )
console.log( arr3 ) // [1, 2, 3, 'a', 'b', 'c', 11, 22, 33, '哇哈哈']
9.2.4 inedxOf
lastInedxOf
inedxOf
: 查找某个元素在数组中首次出现的索引位置,
lastInedxOf
: 查找某个元素在数组中最后一次出现的索引位置,注意:如果找不到返回-1
let arr4 = ['a', 'b', 'c', 'd', 'a', 'b', 'c']
let re4 = arr4.indexOf('c')
let re44 = arr4.lastIndexOf('c')
console.log( re4 ) // 2
console.log( re44 ) // 6
9.2.5 Array.isArray()
- 静态方法
Array.isArray(值)
: 判断是否数组
let n = 123
let nn = [1, 2, 3]
console.log( Array.isArray( n ) ) // false
console.log( Array.isArray( nn ) ) // true
9.2.6 Array.from()
- 静态方法
Array.from(对象)
:把伪数组转换为真数组注意:伪数组必须要有length属性
let o6 = {
0 : 'a',
1 : 'b',
2 : 'c',
3 : 'd',
4 : 'e',
5 : 'f',
length : 6
}
let arr6 = Array.from( o6 )
console.log( arr6 ) // ['a', 'b', 'c', 'd', 'e', 'f']
9.3 Array
构造函数-遍历方法
-
语法
// 数组.forEach( function ( item, index, o ) { // 第一个形参数:代表当前项 // 第二个形参数:代表当前项的索引值 // 第三个形参数:代表当前数组 // } )
9.3.1 forEach
- 遍历数组
forEach
: 遍历数组
let arr = ['张飞', '关羽', 'blue', 'red', 1, 2, 3]
arr.forEach( (item, index, o ) => console.log(item, index, o) )
9.3.2 find
- 遍历查找满足条件的第一个元素并返回
find
: 遍历并查找满足条件的一个元素,并返回
let arr2 = [5, 3, 6, 8, 7, 9, 12, 2]
let re2 = arr2.find( (item) => {
return item > 3
} )
console.log( re2 ) // 5
9.3.3 some
- 遍历查找是否具有满足条件的元素,有一个即为 true
some
: 遍历并查找是否具有满足条件的元素,有一个即为 true
let arr3 = [5, 3, 6, 8, 7, 9, 12, 2]
let re3 = arr3.some( (item, index, o) => {
return item > 3
} )
console.log( re3 ) // true
9.3.4 every
- 遍历并查找是否有满足条件的元素,都满足则为 true
every
: 遍历并查找是否有满足条件的元素,都满足则为true
let arr4 = [5, 3, 6, 8, 7, 9, 12, 2]
let re4 = arr4.every( (item) => {
return item > 3
} )
console.log( re4 ) // false
9.3.5 filter
- 遍历并筛选,把满足条件的值放到新的数组返回
filter
: 遍历并筛选,把满足条件的值放到新的数组返回
let arr5 = [5, 3, 6, 8, 7, 9, 12, 2]
let re5 = arr5.filter( (item) => {
return item % 2 === 0
} )
console.log( re5 ) // [6, 8, 12, 2]
9.3.6 map
- 遍历并让每一个元素执行一遍回调函数,把结果放到数组中返回
map
: 遍历并让每一个元素执行一遍回调函数,把结果放到数组中返回
let arr6 = [5, 3, 6, 8, 7, 9, 12, 2]
let re6 = arr6.map( (item) => {
return item * item
} )
console.log( re6 ) // [25, 9, 36, 64, 49, 81, 144, 4]
9.4 RegExp
构造函数
// RegExp: 是内置的一个构造函数【正则对象】
// 字面量创建:
let reg = /abc/
// 构造函数创建:
let reg = new RegExp(/abc/)
9.5 String
构造函数-方法
// 字面量
let str = 'sadfdadfadfafadfa'
// 构造函数创建
let strr = new String('sadfdadfadfafadfa')
console.log( str, strr);
// 1. length: 字符串的长度,或字符串的个数
let str1 = 'sadfdadfadfafadfa'
console.log(str1.length) // 17
// 2. 索引值
let str2 = 'sadfdadfadfafadfa'
console.log(str1[0]) // s
// 可以循环遍历字符串每一位,不可以用forEach,因为是数组方法,for in 可以
for ( let i = 0; i < str.length; i++ ) {
console.log( str[i] )
}
9.5.1 split
- 分隔字符串为数组
split
: 用于分隔字符串为数组
let str1 = 'abcdefabcder'
let re1 = str1.split('')
console.log(re1) // ['a', 'b', 'c', 'd', 'e', 'f', 'a', 'b', 'c', 'd', 'e', 'r']
9.5.2 indexOf
lastindexOf
indexOf
: 在字符串中查找某个字符,找到返回首次出现的索引值找不到返回-1
lastindexOf
: 在字符串中查找某个字符,找到返回最后依次一次出现的索引值找不到返回-1
let str2 = 'abcdefabcder'
let re2 = str2.indexOf('c')
console.log( re2 ); // 2
let str22 = 'abcdefabcder'
let re22 = str22.lastIndexOf('c')
console.log( re22 ); // 8
9.5.3 toLowerCase
toUpperCase
toLowerCase
: 转换为小写
toUpperCase
: 转换为大写
let str3 = 'abcdEFGHABCabc'
let re3 = str3.toLowerCase( str3 )
console.log( re3 ) // abcdefghabcabc
let str33 = 'abcdEFGHABCabc'
let re33 = str33.toUpperCase( str33 )
console.log( re33 ) // ABCDEFGHABCABC
9.5.4 substring
substr
substring(start, end)
: 截取字符串
substr(start, num)
: 截取字符串
substring(start, end)
: 截取字符串- 从索引值为start开始截取,截取到索引值位置为end的地方
- 注意:不包含end索引位置上对应的字符
substr(start, num)
: 截取字符串- 从索引值为start开始截取,截取几位
- 注意:不包含num索引位置上对应的字符
- 注意:
- 不管是
substring
还是substr
,截取字符串 - 如果只有一个参数,那么就是从这个字符串开始截取到最后
- 不管是
let str4 = 'abcdEFGHABCabc'
let re4 = str4.substring(3, 6)
console.log( re4 ) // dEF
let str44 = 'abcdEFGHABCabc'
let re44 = str44.substr(3, 6)
console.log( re44 ) // dEFGHA
9.5.5 trim
: 去除字符串两端的空格
trim
: 去除字符串两端的空格
let str5 = ' abcdEFG HABCabc '
let re5 = str5.trim( str5 )
console.log(re5) // abcdEFG HABCabc
9.5.6 replace
: 字符串替换
replace
: 字符串替换i: 全局 g: 不区分大小写
let str6 = 'abcaabbcc'
let re6 = str6.replace(/a/gi, 'A')
console.log( re6 ) // AbcAAbbcc
9.6 Number
构造函数 - 方法
// 字面量
let n = 1
console.log( n ) // 1
// 构造函数创建
let m = new Number(123)
console.log( m ) // 123
9.6.1 toFixed
: 保留两位有效数字
toFixed
: 保留两位有效数字
- 注意:该方法有四舍五入
let prive = 36.766161568
let re = prive.toFixed(2)
console.log( re ) // 36.77
9.6.2 parseInt
parseFloat
parseInt
: 取整
parseFloat
: 取浮点数
- 注意:
ES6
不让在全局用,归到Number
构造函数下
let num = '156.682px'
let re2 = Number.parseInt( num )
console.log( re2 ) // 156
let re22 = Number.parseFloat( num )
console.log( re22 ) // 156.682
9.7 Boolean
构造函数
// 字面量
let falg = true
console.log( falg )
// 构造函数
let f = new Boolean(true)
console.log( f )
// 万物皆对象
// 构造函数:let fun = new Function()
// function fun() {}
9.8 公共方法
9.8.1 toString
- 转字符串
toString
:转字符串
// 包装类型、引用类型 都可以用
// toString:转字符串
// 注意:undefined、null 不可以用这个方法,会报错
let num = 123
let arr = [1, 2, 3]
let re = num.toString()
console.log(re) // 123
let ree = arr.toString()
console.log(ree) // 1,2,3
9.8.2 强制转换
String
:构造函数、强制转换
Boolean
: 构造函数、强制转换
Number
: 构造函数、强制转换
// undefined、null 转字符串
// String:构造函数、强制转换
console.log(String(null)) // null
console.log(String(undefined)) // undefined
// Boolean: 构造函数、强制转换
console.log(Boolean(null)) // false
console.log(Boolean(undefined)) // false
console.log(Boolean(123)) // true
// Number: 构造函数、强制转换
console.log(Number(null)) // 0
console.log(Number(undefined)) // NaN
console.log(Number("123")) // 123
9.8.3 valueOf
: 获取原始值
valueOf
: 获取原始值
// valueOf: 获取原始值
let str = new String("abcdefg")
console.log(str.valueOf()) // abcdefg
let time = new Date()
console.log( time.valueOf() ) // 1649823350825(时间戳)
10. 封装
防止变量污染
10.1 命名空间封装
// let uname = '张三丰'
// let age = 22
// let emial = 'zsf@qq.com'
// let uname = 'Hi-ZZ'
// let age = 22
// let emial = 'Hi-ZZ@qq.com'
// 命名空间封装
// 字面量对象:把变量当作属性,把函数当作方法
let user1 = {
uname : '张三丰',
age : 22,
emial : 'zsf3@qq.com'
}
let user2 = {
uname : '张大丰',
age : 28,
emial : 'zsf1@qq.com'
}
let user3 = {
uname : '张二丰',
age : 25,
emial : 'zsf2@qq.com'
}
console.log( user1.uname ) // 张三丰
console.log( user2.uname ) // 张大丰
console.log( user3.uname ) // 张二丰
10.2 构造函数封装
每构建一个实例化对象就会多一个公共方法,浪费空间
// 公共部分
// 姓名 性别 年龄 邮箱
function Person (uname, gender, age, email) {
this.uname = uname
this.gender = gender
this.age = age
this.email = email
this.say = function () {
console.log('你好啊')
}
this.eat = () => console.log('恰饭');
}
let o1 = new Person('张三', '男', '22', 'zsf@qq.com')
console.log( o1 )
let o2 = new Person('Hi', '男', 18, 'Hi@qq.com')
console.log( o2 )
11. 原型对象
prototype
:指向构造函数的原型
constructor
:指回构造函数本身
__proto__
:用来指向原型对象
-
原型对象:构造函数或函数的一个属性
指向了一个对象,我们把这个对象成为原型,或原型对象
-
每一个构造函数都有一个
prototype
:指向构造函数的原型 -
每个原型对象都有一个
constructor
:指回构造函数本身 -
作用:共享成员
function Person(uname, age) {
this.uname = uname
this.age = age
// this.eat = function() {
// console.log('吃饭');
// }
}
// 在构造函数的原型上添加方法
// 【原型对象上的方法构造函数的成员可以用】
Person.prototype.eat = function () {
console.log("吃饭中...")
}
let o1 = new Person("张三", 18)
o1.eat() // 吃饭中...
let o2 = new Person("李四", 20)
let o3 = new Person("王五", 12)
- 每个对象都有一个属性:
__proto__
- 作用:用来指向原型对象
- 当一个对象,访问一个成员的,现在自身查找,如果查不到,沿着
__proto__
继续查找 - 注意:
__proto__
是一个非标准属性
// 构造函数
function Person (uname, age) {
this.uname = uname
this.age = age
this.langage = '汉语'
}
// 原型对象
Person.prototype.eat = function () {
console.log('吃饭中...');
}
Person.prototype.head = 1
// 实例化对象
let obj = new Person('张三', 22)
console.log( obj.__proto__ === Person.prototype ) // true
11.1 原型对象 - 原理图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l6S3yYKC-1650621858212)(./images/YuanXing.png)]
12. 继承
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wgXOjDfj-1650621858215)(./images/JiCheng.png)]
12.1 原型继承
继承: 第一步 把实例对象赋值给原型对象
Chinese.prototype = new Person() // 继承
继承 第二步 让原型上constructor指回自己的构造函数
注意:原型上不存在 constructor, 因为覆盖问题
Chinese.prototype.constructor = Chinese
// 公共构造函数
function Person() {
this.head = 1
this.legs = 2
this.eyes = 2
this.eat = function () {
console.log('吃饭');
}
this.say = function () {
console.log('say方法');
}
}
// 单个构造函数
function Chinese () {
this.language = '汉语'
this.skin = '黄皮肤'
}
function Japanese () {
this.language = '日语'
this.skin = '黄皮肤'
}
function America () {
this.language = '英语'
this.skin = '白皮肤'
}
// 继承
// console.log( Chinese.prototype ) // 没继承前
// 继承 第一步 把实例对象赋值给原型对象
Chinese.prototype = new Person() // 继承
// 继承 第二步 让原型上constructor指回自己的构造函数
// 原型上不存在 constructor, 因为覆盖问题
Chinese.prototype.constructor = Chinese
// console.log( Chinese.prototype ) // 继承后
Japanese.prototype = new Person()
Japanese.prototype.constructor = Japanese
America.prototype = new Person()
America.prototype.constructor = America
// 实例化
let c = new Chinese()
console.log( c )
let j = new Japanese()
console.log( j )
let a = new America()
console.log( a )
12.2 原型链
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jB0cV2HA-1650621858217)(./images/YuanXingLian.png)]
function People () {
this.head = 1
this.legs = 2
}
function Chinese () {
this.language = '汉语'
this.skin = '黄皮肤'
}
// 继承
Chinese.prototype = new People()
Chinese.prototype.constructor = Chinese
let c1 = new Chinese()
console.log( c1 )
console.log(c1.constructor);
// 原型链上添加方法
Object.prototype.lanqiu = function () {
console.log( '打篮球...' )
}
// 原型链上添加方法
People.prototype.lanqiu = function () {
console.log( '...打篮球...' )
}
let c2 = new Chinese()
c2.lanqiu() // ...打篮球...
13. this
指向
- 重点:谁调用,
this
指向谁】- 严格模式下普通函数
this
为undefined】- 箭头函数
this
之指向所在作用域的this;或者上级作用域的this
】
// 1. 普通函数
function fn() {
console.log(this) // window、调用者
}
fn()
// 2. 事件处理函数
document.addEventListener("click", function () {
console.log(this) // 事件源、调用者
})
// 3. 构造函数
function Person() {
console.log(this) // 实例对象
}
new Person()
// 4. 方法
let obj = {
uname: "张飞",
fei: function () {
console.log(this) // obj、调用者
},
}
obj.fei()
// 5. 定时器
setTimeout(function () {
console.log(this) // window、调用者
}, 1000)
// 6.
// 7. 箭头函数
let o = {
uname: "阿飞",
fei: function () {
// console.log(this) // o
// setTimeout( function () {
// console.log(this) // window
// }, 1000)
setTimeout( () => {
console.log(this) // o
}, 1000)
}
}
o.fei()
14. 改变this
指向 - call
call
: 他是函数的一个方法
- 语法:函数.call(this, 参数1, 参数2, …)
14.1 call
- 函数的方法-改变this指向
// call: 他是函数的一个方法
// 语法:函数.call(this, 参数1, 参数2, ...)
// 执行过程:函数调用了call方法,call会回头执行函数,在执行函数的过程中this会指向call的第一个参数
// 使用场景:想改函数里this指向的时候
function fn(a, b) {
console.log(this, a, b);
}
// fn()
let obj = {uname : '阿飞', age : 22}
// fn函数调用了call方法,call会回头执行fn函数,在执行函数的过程中this会指向call的第一个参数
fn.call(obj, 1, 2)
14.2 apply
- 函数的方法-改变this指向
- apply: 他是函数的一个方法
- 语法:函数.call(this, 参数1, 参数2, …)
// 2. apply: 他是函数的一个方法
// 语法:函数.apply(this, [参数1, 参数2, ...])
// 执行过程:函数调用了apply方法,apply会回头执行函数,在执行函数的过程中this会指向apply的第一个参数
// 注意:apply的参数第一个是this的指向对象,第二个是一个数组,数组里面是任意个参数
// 使用场景:既要改变this指向,又要设计数组的时候
function fn2(a, b) {
console.log(this);
console.log(a, b);
}
let obj2 = {
uname : '哇哈哈',
age : 22
}
fn2.apply(obj2, [11, 22])
// 使用举例
let arr = [23, 25, 69, 84, 92, 62]
let re = Math.max.apply(null, arr)
console.log(re) // 92
// ...:剩余值(接收参数时)
// ...:扩展运算符(复杂值时)
console.log(...arr) // 23 25 69 84 92 62
let re2 = Math.max(...arr)
console.log(re2) // 92
14.3 bind - 函数的方法-改变this指向
- bind: 他是函数的一个方法
- 语法: 函数.bind(this, 参数1, 参数2, …)
// 3. bind: 他是函数的一个方法
// 语法: 函数.bind(this, 参数1, 参数2, ...)
// 执行过程:函数调用了bind方法,bind不会回头执行函数,而是悄悄改变了函数的this指向,然后返回新的函数
// 使用场景:需要改变this,但是不需要立马执行函数
function fn3(a, b) {
console.log(this)
console.log(a, b)
}
let o3 = { uname: "aaa" }
let re3 = fn3.bind(o3, 111, 222)
re3()
// 使用举例:
let btn = document.querySelector("input")
btn.addEventListener("click", function () {
// 禁用元素
this.disabled = true
setTimeout(
function () {
// 启用元素
this.disabled = false
}.bind(this),3000) // 这里用call改变函数内this指向会回头立马执行函数;这行显示的this是事件源
})
15. 严格模式
- 严格模式:严格
- 开启严格模式:“use strict”
- 放到哪个作用域哪个作用域就遵守严格模式
- 注意:必须放到作用域的开头
// 开启严格模式后
// 变量必须生说明
// n = 1
// console.log( n ) // 报错
// 普通函数中的 this,不在指向window,而是undefined
function fn () {
console.log(this) // undefined
}
fn()
// 函数形参不可以重名
// function fun(a, a) {
// console.log(a + a) // 报错
// }
// fun()
16. 类 - class
16.1 类和对象
// ES6之前
// 模板:
// function Person () {}
// 人:泛泛
// 姚明: 高, 篮球, 有钱
// ES6
// 类:泛泛概念
// 对象:具体概念
// 可以确定下来具体的东西是对象,确定不下来不够具体的是类
16.2 类的基本使用
- 创建类:
- 语法:class 类名 {}
- 注意:类名首字母大写
- 实例化:
- 语法:new 类名()
// 创建类
class Person {}
// 实例化
let p1 = new Person()
console.log(p1);
// 检测实例化的对象是不是对象,是对象的话就是Object构造出来的
// console.log( p1 instanceof Object ) // true
// 练习创建类
class Dog {}
let hei = new Dog()
console.log(hei);
16.3 类的操作 - 添加成员 - static
添加成员:
- 实例成员 - 属性名 = 属性值
- 静态成员 -
static
注意:方法直接自动添加到原型上了(Person.prototype)
// 1. 添加实例成员
// 创建类
class Person1 {
// 实例属性 (定义变量不加let)
uname = '张三丰'
age = 22
head = 1
// 实例方法 (定义函数不加function)
// 方法直接自动添加到原型上了(Person.prototype)
eat () {
console.log('eat');
}
say () {
console.log('say');
}
}
// 实例化
let o1 = new Person1()
console.log(o1);
// 2. 添加静态成员
// 创建类
class Person2 {
// 静态属性
static language = '汉语'
static sjin = '黄皮肤'
// 静态方法
static sleep () {
console.log('睡觉');
}
static walk () {
console.log('走路');
}
}
// 实例化
let o2 = new Person2()
console.log( o2 )
// console.log( o2.language ) // undefined
console.log( Person2.language ) // 汉语
16.4 类的操作 - 构造函数 - constructor () {}
构造函数、构造方法、构造器
constructor () {}
注意:
- 构造方法,在new的时候自动执行
- 构造方法,如果不写的话,那么内部会默认的添加上
- 构造方法,名字是固定的,不可以更改
- 构造方法,用来接收参数,做一些初始化的操作
- 一个类里面只能有一个构造方法,顺序无要求,建议写前面
// 创建类
class Person {
head = 1
constructor (uname, age, gender) {
// 类里面固定的方法,用来接收参数,初始化操作,不可以改名
this.uname = uname
this.age = age
this.gender = gender
}
say () {
console.log('say')
}
eat () {
console.log('eat')
}
}
// 实例化 (注意方法在原型对象上)
let o1 = new Person('张飞', 22, '男')
console.log(o1) // Person {head: 1, uname: '张飞', age: 22, gender: '男'}
let o2 = new Person('关羽', 23, '男')
console.log(o2) // Person {head: 1, uname: '关羽', age: 23, gender: '男'}
let o3 = new Person('貂蝉', 21, '女')
console.log(o3) // Person {head: 1, uname: '貂蝉', age: 21, gender: '女'}
16.5 类的操作 - 继承 - extends
、super
extends
: 申明一个类为子类
super
: 调用父类的方法
class Father {
constructor (uname, age) {
this.uname = uname
this.age = age
}
qian () {
console.log('转它一个亿');
}
}
// extends: 申明一个类为子类
// Son为子类,Father为父类
class Son extends Father {
constructor ( uname, age, score ) {
// 在继承时,如果子类有自己的constructor,那么必须使用super调用父类的方法
// super: 调用父类的方法
super(uname, age)
this.score = score
// 注意:必须先用super调用父级的构造方法,在用自己的构造方法
}
qian () {
super.qian()
// console.log('转它半个亿');
}
}
// 实例化
let s1 = new Son('儿子', 2, 99)
console.log(s1) // Son {uname: '儿子', age: 2, score: 99}
s1.qian() // 赚它一个亿
16.6 类的本质
- 类的本质:函数
- 还是哪个构造函数function Person(){}
- 语法糖、语法盐
- i++ 是 i = i + 1 的语法糖
- i = i + 1 是 i++ 的语法盐
// 构造函数
// function Person() {}
// let o = new Person()
// 类
class Person {}
let o = new Person()
// console.log( typeof Person ) // function
// console.log( Person.prototype )
// console.log( Person.__proto__ )
// 类的本质:函数
// 还是哪个构造函数function Person(){}
// 语法糖
// 语法盐
// 例:i = i + 1 和 i++
// i++ 是 i = i + 1 的语法糖
// i = i + 1 是 i++ 的语法盐
17. 拷贝
新对象 = JSON.parse( JSON.stringify(旧对象) )
17.1 拷贝-浅拷贝
let obj = {
uname : '张三丰',
age : 22,
gender : '男',
message : {
index : 6,
score : 99
}
}
let newObj = {}
// 浅拷贝 有个方法 assign
// 语法:Objecy.assign( newObj, obj )
// Object.assign( newObj, obj )
// 浅拷贝-原理
// 遍历
// for ( let key in obj ) {
// newObj[key] = obj[key]
// }
17.2 拷贝-深拷贝
// 封装深层拷贝函数
function kaobei(newObj, obj) {
for (let key in obj) {
// 必须先判断数组,因为数组也是对象,对象不是数组
if (obj[key] instanceof Array) {
// obj[key]可能是数组
newObj[key] = []
kaobei(newObj[key], obj[key])
} else if (obj[key] instanceof Object) {
// obj[key]可能是对象
newObj[key] = {}
kaobei(newObj[key], obj[key])
} else {
newObj[key] = obj[key]
}
}
}
let obj = {
uname: "张三丰",
age: 22,
gender: "男",
message: {
index: 6,
score: 99,
},
}
let newObj = {}
kaobei(newObj, obj)
JavaScript 案例练习
1. 练习 - 数组去重
let arr = ['a', 'b','a', 'c','a', 'b','c', 'd']
let newArr = []
// 思路:依次获取每个元素,看看新数组里有没有,有就不管,没有就添加
for ( let i = 0; i < arr.length; i++ ) {
if ( newArr.indexOf( arr[i] ) === -1 ) newArr.push( arr[i] )
}
console.log( newArr ) // ['a', 'b', 'c', 'd']
2. 练习 - 数组单元满足条件的返回
let arrr = [
{gname : '华为', prive : '6000', num : 3},
{gname : '三星', prive : '3666', num : 6},
{gname : '苹果', prive : '11666', num : 9},
{gname : '锤子', prive : '5888', num : 11}
]
let ree = arrr.filter( (item, index, o) => {
return item.prive >= 5000 && item.prive <= 10000
} )
console.log( ree ) // 输出为下
// 0: {gname: '华为', prive: '6000', num: 3}
// 1: {gname: '锤子', prive: '5888', num: 11}