JavaScript重点知识总结一
感兴趣的朋友可以去我的语雀平台进行查看更多的知识。
https://www.yuque.com/ambition-bcpii/muziteng
1. 基础
1.1 变量关键字
JavaScript
使用专门的关键字let
和var
来声明(定义)变量,在使用时需要注意一些细节
let
注意事项
- 不允许重复声明
- 只能在块级作用域内使用
- 不存在变量提升,必须声明变量之后才能使用,不然会报
Uncaught ReferenceError
错误 JavaScript
中内置的一些关键字不能被当做变量名
var
注意事项
- 允许重复声明
- 可以在任意位置使用
- 可以在声明语句之前使用
大部分情况使用let
和var
区别不大,但是let
相较var
更严谨,因此推荐使用let
1.2 数据类型
特点
js
是弱类型动态语言,只有程序运行过程中才能确定类型- 变量的数据类型是可以变化的
- 注:
JavaScript
中变量的值决定了变量的数据类型。
数据类型的分类
- 简单数据类型(Number String Boolean Undefined Null)
- 复杂数据类型(Object)
1.2.1 简单数据类型
简单数据类型 | 说明 | 默认值 |
---|---|---|
Number | 数字型,整数或浮点数 | 0 |
Boolean | 布尔值true false | false |
String | 字符串 | “” |
Undefined | 变量只实名还没有赋值 | undefined |
Null | 空值 | null |
bigint | 是一种数字类型的数据,它可以表示任意精度格式的整数 | |
symbol |
数字型:即我们数学中学习到的数字,可以是整数、小数、正数、负数
-
最大值
Number.MAX_VALUE
,比最大值大就是无穷大Infinty
-
最小值
Number.MIN_VALUE
,比最小值小就是无穷大-Infinty
-
非数字
NaN
isNaN()
判断是否非数字 -
undefined
和数字相加最后的结果是NaN
1.2.2 数据类型转换
转换为字符串
转换为数字型(重点)
转换为布尔型
1.2.3 instanceof
instanceof(判断实例方法)
- 专门判断对象的具体类型
instanceof
运算符用于检测构造函数的prototype
属性是否出现在某个实例对象的原型链上
var b1 = {
b2: [1, 'abc', console.log],
//可以简化成 b3:()=>()=> 'hongjilin' -->高阶函数相关知识
b3: function () {
return () =>{ return 'hongjilin'}
}
}
/**使用instanceof进行对象判断*/
console.log(b1 instanceof Object, b1 instanceof Array) // true false
console.log(b1.b2 instanceof Array, b1.b2 instanceof Object) // true true
console.log(b1.b3 instanceof Function, b1.b3 instanceof Object) // true true
/**使用typeof进行对象中某属性的判断*/
console.log(typeof b1.b2, typeof null) // 'object' 'object'
console.log(typeof b1.b3='function') // true
console.log(typeof b1.b2[2]='function') //true
/**调用对象与数组中某函数示例*/
b1.b2[2]('调用console.log打印hongjilin') //调用console.log打印hongjilin
console.log(b1.b3()()) // hongjilin
1.3 函数
函数形参和实参个数不匹配问题
arguments
实际上它是当前函数的一个内置对象。所有函数都内置了一个arguments
对象,arguments
对象中存储了传递的所有实参。
1.4 作用域与内置对象
1.4.1 作用域
只要是代码,就至少有一个作用域,写在函数内部的局部作用域,如果函数中还有函数,那么在这个作用域中就又可以诞生一个作用域
根据在内部函数可以访问外部函数变量的这种机制,用链式查找决定哪些数据能被内部函数访问,就称作用域链,作用域链就是采取就近
原则的方式来查找变量最终的值。
function f1() {
var num = 123;
function f2() {
console.log( num );
}
f2();
}
var num = 456;
f1();
1.4.2 内置对象
JavaScript
中的对象分为3种
- 自定义对象,属于
ECMAScript
- 内置对象,属于
ECMAScript
- 浏览器对象,属于
JS独有
的
内置对象:指JS
语言自带的一些对象,供开发者使用,提供了一些常用的最基本而必要的功能(属性和方法)
- 内置对象最大的优点就是帮助我们快速开发
JavaScript
提供了多个内置对象:Math
、Date
、Array
、String
等
文档:MDN
1.4.2.1 Math对象
Math.PI // 圆周率
Math.floor() // 向下取整
Math.ceil() // 向上取整
Math.round() // 四舍五入版 就近取整 注意 -3.5 结果是 -3
Math.abs() // 绝对值
Math.max()
Math.min() // 求最大和最小值
Math.random() // 随机数方法,返回一个随机的小数 0 =< x < 1
1.4.2.2 Date对象
Date
实例用来处理日期和时间。
Date
对象和Math
对象不一样,他是一个构造函数,所以我们需要实例化后才能使用
let date = new Date();
console.log(date); // Sun Sep 11 2022 19:17:01 GMT+0800 (中国标准时间)
获取日期的总的毫秒形式
Date
对象是基于1970年1月1日(世界标准时间)起的毫秒数,常利用总的毫秒数来计算时间,因为它更精确
valueOf()
getTime()
new Date()
Date.now()
获得距离1970年1月1号过了多少毫秒数
1.4.2.3 数组对象
数组对象的创建
-
字面量方式
[]
-
new Array([length])
new Array(...元素)
检测是否为数组
instanceof
运算符,可以判断一个对象是否属于某种类型Array.isArray()
用于判断一个对象是否为数组,isArray()
是HTML5
中提供的方法
添加删除数组元素的方法
注意:sort()
默认按照比较字符串大小的方式一个字符一个字符的比,数字也是,除非传入一个方法
slice(start, end)
start
可以为负数,表示从数组尾部开始算起的位置。-1指最后一个元素,-2指倒数第二个元素,以此类推end
如果没有被指定,表示到最后。若指定,表示左闭右开 [start,end)- 方法不会修改数组本身,而是返回一个新数组
splice(start[, deleteCount[, item1[, item2[, ...]]]])
start
表示从什么位置开始添加或删除数组元素deleteCount
表示删除的元素数量,如果为0,则表示不删除数组元素item1[, item2[, ...]]]]
表示新增的数组元素- 方法会改变原始数组
let arr1 = [13, 4, 77, 1, 7];
arr1.sort((a, b) => a - b); // 升序
console.log(arr1)
arr1.sort((a, b) => b - a); // 降序
console.log(arr1)
console.log(arr1.join()) // 默认为,
let numbers = arr1.slice(1, 4); // 4,77,1 [begin,end) 左闭右开
console.log(numbers)
arr1.splice(1, 3); // [13,7]
console.log(arr1)
1.4.2.4 字符串对象
字符串的不可变
指的是里面的值不可变,虽然看上去可以改变内容,但其实是地址变了,内存中新开辟了一个内存空间。
replace
(被替换的字符串, 替换为的字符串) 用于在字符串中用一些字符替换另一些字符,只替换第一个
split()
用于切分字符串,它可以将字符串切分为数组。在切分完毕之后,返回的是一个新数组。
toUpperCase()
转换大写
toLowerCase()
转换小写
includes()
方法用于判断一个字符串是否包含在另一个字符串中,根据情况返回 true 或 false
2. 进阶
2.1 函数
2.1.1 回调函数
- 你定义的
- 你没有调
- 但最终它执行了(在某个时刻或某个条件下)
常见的回调函数
dom
事件回调函数 ==> 发生事件的dom元素- 定时器回调函数 ===> window
ajax
请求回调函数- 生命周期回调函数
// dom事件回调函数
document.getElementById("btn").onclick = function () {
alert(this.innerHTML);
}
// 定时器回调函数
setTimeout(function () {
alert("到点了" + this)
}, 2000);
2.1.2 函数中的this
this
- 任何函数本质上都是通过某个对象来调用的,如果没有直接指定就是window
- 所有函数内部都有一个变量 this
- 它的值是调用函数的当前对象
如何确定this的值?
- test() window
- p.test() p
- new test() 新创建的对象
- p.call(obj) obj
function Person(color) {
console.log(this);
this.color = color;
this.getColor = function () {
console.log(this)
return this.color;
}
this.setColor = function (color) {
console.log(this)
this.color = color;
}
}
const obj = {};
Person("red"); //this是谁? window
const p = new Person("yello"); //this是谁?p
p.getColor();
// 调用call会改变this指向--->让我的p函数称为obj的临时方法及逆行调用
p.setColor.call(obj, "black"); //this是谁?obj
const test = p.setColor;
test(); //this是谁?window
2.1.3 原型与原型链
2.1.3.1 原型prototype
函数的prototype
属性
-
每个函数都有一个
prototype
属性,它默认指向一个Object
空对象(即称为原型对象) -
原型对象中有一个属性
constructor
,它指向函数对象
给原型对象添加属性(一般都是方法)
- 作用: 函数的所有实例对象自动拥有原型中的属性(方法)
// 每个函数都有一个prototype属性,它默认指向一个Object空对象(原型对象)
console.log(Date.prototype, typeof Date.prototype);
function Fun() {
}
console.log(Fun.prototype) // 默认指向一个Object空对象(没有自己的属性)
// 原型对象中有一个属性constructor,它指向函数对象
console.log(Date.prototype.constructor === Date) // true
console.log(Fun.prototype.constructor === Fun) // true
// 给原型对象添加属性、方法(一般是方法) ===> 实例对象可以访问
Fun.prototype.test = function () {
console.log("test()")
}
let fun = new Fun();
fun.test();
2.1.3.2 显示原型与隐式原型
- 每个函数
function
都有一个prototype
,即显式原型属性 - 每个实例对象都有一个
__proto__
,可称为隐式原型属性 - 对象的隐式原型的值为其对应构造函数的显式原型的值
// 定义构造函数
function Fn() {
// 内部默认执行语句:this.prototype = {}
}
// 1.每个函数function都有一个prototype,即显示原型属性,默认指向一个空的Object对象
console.log(Fn.prototype)
// 创建实例对象
let fn = new Fn() // 内部默认执行语句:this.__proto__ = Fn.prototype
// 2.每个实例对象都有一个__proto__,可称为隐式原型
console.log(fn.__proto__)
// 3.对象的隐式原型的值为其对应构造函数的显示原型的值
console.log(Fn.prototype === fn.__proto__) // true
// 给原型添加方法
Fn.prototype.test = function () {
console.log("test()")
}
// 通过实例对象调用原型方法
fn.test(); // test()
内存结构
总结
-
函数的
prototype
属性:在定义函数时自动添加的,默认值是一个空Object
实例对象(Object 函数除外,因为__proto__
属性为null)
-
对象的
__proto__
属性:创建对象时自动添加的,默认值为构造函数的prototype
属性值,Function函数
-
程序员可以直接操作显式原型,但不能直接操作隐式原型(ES6之前)
2.1.3.3 原型链
原型链
访问一个对象的属性时
- 先在自身属性中查找,找到返回
- 如果没有, 再沿着
__proto__
这条链向上查找,找到返回 - 如果最终没找到,返回
undefined
- 别名:隐式原型链
- 作用:查找对象的属性(方法)
构造函数/原型/实例对象的关系(图解)
var o1 = new Object();
var o2 = {};
所有函数的__proto__
都是一样的
- 函数现实原型默认值是一个空Object实例对象(Object 函数除外,因为__ proto __ 属性为 null)
- 所有函数都是
Function
函数的实例,包括Function
本身 Object
的原型对象是原型链的尽头Object.prototype.__proto__
为 null
属性问题
读取对象的属性值时:会自动到原型链中查找
设置对象的属性值时:不会查找原型链,如果当前对象中没有此属性,直接添加此属性并设置其值
方法一般定义在原型中,属性一般通过构造函数定义在对象本身上
2.1.4 闭包
返回函数的函数[高阶函数]
let val = 7
function createAdder() {
function addNumbers(a, b) {
let ret = a + b
return ret
}
return addNumbers
}
let adder = createAdder()
let sum = adder(val, 8)
console.log('example of function returning a function: ', sum)
高阶函数:就是一个函数就可以接收另一个函数作为参数,或者是返回一个函数–>常见的高阶函数有map、reduce、filter、sort等
let add = function add(a) {
return function (b) {
return a + b;
}
}
console.log(add(2)(3)); //5
map
// map接受一个函数作为参数,不改变原来的数组,返回一个全新的数组
let arr = [1, 2, 3, 4, 5];
let arr2 = arr.map(item => item * 2);
console.log(arr2) // [2,4,6,8,10]
reduce
let arr = [1, 2, 3, 4, 5];
// reduce接受一个函数作为参数,这个函数要有两个形参,代表数组中的前两项
// reduce会将这个函数的结果与数组中的第三项再次组成这个函数的两个形参以此类推进行累计操作
let arr3 = arr.reduce((a, b) => a + b);
console.log(arr3); // 15
filter
let arr = [1, 2, 3, 4, 5];
// filter返回过滤后的数组。filter也接受一个函数作为参数,这个函数作用于数组中的每个元素
// 根据该函数每次执行后返回的布尔值来保留结果,如果是true就保留,如果是false就过滤掉
let arr4 = arr.filter(item => item % 2 === 0);
console.log(arr4); // [2,4]
…TODO
2.2 面向对象
2.2.1 继承模式
2.2.1.1 原型链继承
- 套路
- 定义父类型构造函数
- 给父类型的原型添加方法
- 定义子类型的构造函数
- 创建父类型的实例对象赋值给子类型的原型
- 将子类型原型的构造属性设置为子类型
- 给子类型原型添加方法
- 创建子类型的对象,可以调用父类型的方法
- 关键
- 子类型的原型为父类型的一个实例对象
// 父类型
function Supper() {
this.supProp = '父亲的原型链'
}
// 给父类型的原型上增加一个 showSupperProp 方法,打印自身 subProp
Supper.prototype.showSupperProp = function () {
console.log(this.supProp)
}
// 子类型
function Sub() {
this.subProp = '儿子的原型链'
}
// 子类型的原型为父类型的一个实例对象
Sub.prototype = new Supper()
// 让子类型的原型的constructor指向子类型
// 如果不加,其构造函数找的[`new Supper()`]时从顶层Object继承来的构造函数,指向[`Supper()`]
Sub.prototype.constructor = Sub
// 给子类型的原型上增加一个 showSubProp 方法,打印自身 subProp
Sub.prototype.showSubProp = function () {
console.log(this.subProp)
}
var sub = new Sub()
sub.showSupperProp() // 父亲的原型链
sub.showSubProp() // 儿子的原型链
console.log(sub)
/**
Sub {subProp: "儿子的原型链"}
subProp: "儿子的原型链"
__proto__: Supper
constructor: ƒ Sub()
showSubProp: ƒ ()
supProp: "父亲的原型链"
__proto__: Object
*/
示例图
注意:此图中没有体现 constructor 构造函数,会在下方构造函数补充处指出
Sub.prototype.constructor = Sub
如果不加,会指向 Supper
2.2.1.2 借用构造函数继承(假的)
套路
- 定义父类型构造函数
- 定义子类型构造函数
- 在子类型构造函数中调用父类型构造
关键
- 在子类型构造函数中通用call()调用父类型构造函数
作用
- 能借用父类中的构造方法,但是不灵活
function Person(name, age) {
this.name = name
this.age = age
}
function Student(name, age, price) {
// 此处利用call(),将 [Student]的this传递给Person构造函数
Person.call(this, name, age) // 相当于: this.Person(name, age)
/*this.name = name
this.age = age*/
this.price = price
}
var s = new Student('Tom', 20, 14000)
console.log(s.name, s.age, s.price)
Person
中的this
是动态变化的,在[Student]中利用[Person.call(this, name, age)]改变了其this指向,所以可以实现此效果
2.2.1.3 组合继承
原型链 + 借用构造函数的组合继承
- 利用原型链实现对父类型对象的方法继承
- 利用
super()
借用父类型构建函数初始化相同属性
function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype.setName = function (name) {
this.name = name
}
function Student(name, age, price) {
Person.call(this, name, age) // 为了得到属性
this.price = price
}
Student.prototype = new Person() // 为了能看到父类型的方法
Student.prototype.constructor = Student // 修正constructor属性
Student.prototype.setPrice = function (price) {
this.price = price
}
var s = new Student('Tom', 24, 15000)
s.setName('Bob')
s.setPrice(16000)
console.log(s.name, s.age, s.price)