首先我们想掌握好基础知识点,就要自己尝试着去进行知识点梳理!这一步很重要,脑中要有一个知识点的树才行。
简单梳理基础的知识点:
- 变量类型
js的数据类型分类和判断
值类型和引用类型
复制代码
- 原型和原型链(继承)
原型和原型链的定义
继承写法
复制代码
- 作用域和闭包
执行上下文
this
闭包是什么
复制代码
- 异步
同步vs异步
异步和单线程
前端异步场景
复制代码
- ES6/7新标准的考查
箭头函数
Module
Class
Set和Map
Promise
等等..........
复制代码
知识点梳理
基础知识体系搭建完后,我们可以进行对知识体系的详细完善
变量类型
js是一种弱类型脚本语言,就是在定义的时候不需要定义类型,在程序运行过程中会自动判断其类型的。
ES中的6中原始类型:
- Null
- Undefined
- String
- Number
- Boolean
- Symbol(ES6新增)
typeof
typeof得到的值有以下几种类型: undefined boolean number string object
function 、 symbol
值得注意的是:
1.typeof null 结果是 object,null是原始值,非引用类型
2.typeof不能检测出数组
3.typeof Symbol() 用 typeof 获取 symbol 类型的值得到的是 symbol ,这是 ES6 新增的知识点
复制代码
instanceof
如判断一个变量是否是数组,使用 typeof 无法判断,但可以使用 [1,
2] instanceof Array 来判断。因为, [1, 2] 是数组,它的构造函数就是 Array
值类型 vs 引用类型
根据 JavaScript 中的变量类型传递方式,又分为值类型和引用类型,值类型变量包括 Boolean、
String、Number、Undefined、Null,引用类型包括了 Object 类的所有,如 Date、Array、Function
等。
在参数传递方式上,值类型是按值传递(赋值给其他变量原值不发生改变),引用类型是按共享传递(赋值给其他变量其实是把地址共享出去了,其他变量发生改变也会导致堆中的值发生改变,因为他们指向的是同一个地址)。
JS 中这种设计的原因是:
按值传递的类型,复制一份存入栈内存,这类类型一般不占用太多内存,而且按值传递保证了其访问速度。
按共享传递的类型,是复制其引用,而不是整个复制其值(C 语言中的指针),
保证过大的对象等不会因为不停复制内容而造成内存的浪费。
例子:
var obj = {
a: 1,
b: [1,2,3]
}
var a = obj.a
var b = obj.b
a = 2
b.push(4)
console.log(obj, a, b)
复制代码
原型和原型链
原型
- JavaScript 是基于原型的语言,有以下四点要记住理解:
- 所有的引用类型(数组、对象、函数),都具有对象特性,即可自由扩展属性( null 除外)
- 所有的引用类型(数组、对象、函数),都有一个 _ proto _ 属性,属性值是一个普通的对象
- 所有的引用类型(数组、对象、函数), _ proto _ 属性值指向它的构造函数的 prototype 属性值(实例._ proto _ === 构造函数.prototype)
- 所有的函数,都有一个 prototype 属性,属性值也是一个普通的对象
- 当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么会去它的 _ proto _ (即它的构造函数的 prototype )中寻找
- 使用 hasOwnProperty判断这个属性是不是对象本身的属性:
高级浏览器已经在 for in 中屏蔽了来自原型的属性,但有时候加上原型的判断,可以保证
程序的健壮性:
f.hasOwnProperty(item) => 判断item是否是f对象本身的属性
复制代码
原型链
当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性(toString),
那么会去它的 _ proto _ (即它的构造函数的 prototype )中寻找。如果在 f._ proto _ 中没有找到 toString ,那么就继续去 f._ proto _ ._ proto _ 即 Foo.prototype._ proto _ 中寻找。
这样一直往上找,你会发现是一个链式的结构,所以叫做“原型链”。如果一直找到最上层都没有找到,
那么就宣告失败,返回 undefined 。最上层是什么?是 Object.prototype._ proto _ === null
原型链中的this
很复杂!请看我前几篇的文章中关于this的描述
作用域和闭包
执行上下文
在一段 JS 脚本(即一个 <script> 标签中)执行之前,
要先解析代码(所以说 JS 是解释执行的脚本语言),
解析的时候会先创建一个 全局执行上下文 环境,
先把代码中即将执行的(内部函数的不算,因为你不知道函数何时执行)变量、函数声明都拿出来。
变量先暂时赋值为 undefined ,函数则先声明好可使用。
这一步做完了,然后再开始正式执行程序。再次强调,这是在代码执行之前才开始的工作。
一个函数在执行之前,也会创建一个 函数执行上下文 环境,跟 全局上下文 差不多,不过 函数执
行上下文 中会多出 this arguments 和函数的参数
复制代码
总结一下:
范围:可执行的js代码
全局上下文:变量定义,函数声明
函数上下文:变量定义,函数声明, this , arguments
this
this 的值是在执行的时候才能确认,定义的时候不能确认! 因为 this 是执行上下文环境创建的时候确认的
注意几个使用场景:构造函数中,对象属性执行,普通函数执行,call,apply,bind改变指向
作用域
最初JS 没有块级作用域,只有全局作用域和函数作用域。但会有污染暴露的危险。所以我们用的jQuery,Zepto等库都会放在 (function(){….})()私有变量,防止内存泄漏。
ES6 中开始加入了块级作用域,使用 let 定义变量即可
作用域链
自由变量:当前作用域没有定义的变量,自由变量的值只能一一向父级作用域寻找,这种一层层的关系就是作用域链。而且作用域链是创建指向上下文的时候创建的,所以是在定义变量的时候就确定了作用域链。
闭包
{ }块 或者函数内部有内部函数,且内部函数引用了外部的变量,这种情况就会形成闭包
异步
同步vs异步
- 异步:不会阻塞后面程序的运行,可以和其他时间线同时进行
- 同步:像是一个个要排队的人,不能插队,只能一个个来,前面的会堵塞后面的程序
异步和单线程
js是单线程运行的,只能在同一时间只做一件事情,不能“一心二用”。
// 我们来看这个例子
var a = true;
setTimeout(function(){
a = false;
}, 100)
while(a){
console.log('while执行了')
}
// qishi henduor yiwei zheli hui 100mszhihou while会终止,实际不是这样的,
// 因为js 是单线程的,进入while之后没有时间线程去跑定时器,所以其实这里是个死循环
复制代码
前端异步的场景
很多的呀~定时器延时器啊,网络请求,img加载,ajax等等
// img 代码示例(常用于打点统计)
console.log('start')
var img = document.createElement('img')
// 或者 img = new Image()
img.onload = function () {
console.log('loaded')
img.onload = null
}
img.src = 'https://juejin.cn/xxx.png'
console.log('end')
复制代码
ES6/7 新标准的考查
箭头函数
箭头函数是ES6中新定义的函数定义形式
function name(a1,a2){…} 可以写成 (a1,a2) => {…}
箭头函数和传统函数还是有挺多区别的,可以看我写的其他文章,里面有详细讲解到,箭头函数写起来更加简洁,而且还能解决es6之前函数执行中this是全局变量的问题,例子如下所示:
function fn() {
console.log('real', this) // {a: 100} ,该作用域下的 this 的真实的值
var arr = [1, 2, 3]
// 普通 JS
arr.map(function (item) {
console.log('js', this) // window 。普通函数,这里打印出来的是全局变量,令人费
解
return item + 1
})
// 箭头函数
arr.map(item => {
console.log('es6', this) // {a: 100} 。箭头函数,这里打印的就是父作用域的
this
return item + 1
})
}
fn.call({a: 100})
复制代码
Module
ES6 中模块化语法更加简洁
// 创建 util1.js 文件,内容如
export default {
a: 100
}
// 创建 index.js 文件,内容如
import obj from './util1.js'
console.log(obj)
复制代码
如果想要输出多个对象,就不能用 default 了,且 import 时候要加 {…} ,我们一般在写api的时候,调用引入接口的时候,也是这样写的,还有引入一些组件啊方法啊,都是这样引入,可以简化代码
// 创建 util2.js 文件,内容如
export function fn1() {
alert('fn1')
}
export function fn2() {
alert('fn2')
}
// 创建 index.js 文件,内容如
import { fn1, fn2 } from './util2.js'
fn1()
fn2()
复制代码
Class
class和普通的构造函数又有什么区别呢?
class 其实一直是 JS 的关键字(保留字),但是一直没有正式使用,直到 ES6 。
ES6的class就是取代之前构造函数 初始化 对象的形式,语法上更加符合面向对象的写法。
给大家看两个例子:
// js构造函数的写法:
function MathHandle(x, y) {
this.x = x;
this.y = y;
}
MathHandle.prototype.add = function () {
return this.x + this.y;
};
var m = new MathHandle(1, 2);
console.log(m.add())
// ES6的class写法:
class MathHandle {
constructor(x, y) {
this.x = x;
this.y = y;
}
add() {
return this.x + this.y;
}
}
const m = new MathHandle(1, 2);
console.log(m.add())
复制代码
关于class有5点要注意的:
- class是一种新的语法形式,是class Name {…}这种形式写的
- 两者对比,class的构造函数的函数体要写在constructor中,constructor 即构造器,初始化实例时默认执行
- class中函数的写法是add(){…},并没有function关键字
- 使用extends可以实现继承,更加符合面向对象的语言写法
- 子类的constructor一定要执行super(),以调用父类的constructor
给大家看两个例子:
- js构造函数实现的继承:
// 动物
function Animal() {
this.eat = function () {
console.log('animal eat')
}
}
// 狗
function Dog() {
this.bark = function () {
console.log('dog bark')
}
}
Dog.prototype = new Animal()
// 哈士奇
var hashiqi = new Dog()
console.log(hashiqi) // {bark: ƒ (),__proto__:Animal}
复制代码
- ES6 class实现的继承
class Animal {
constructor(name) {
this.name = name
}
eat() {
console.log(`${this.name} eat`)
}
}
class Dog extends Animal {
constructor(name) {
super(name)
this.name = name
}
say() {
console.log(`${this.name} say`)
}
}
const dog = new Dog('哈士奇')
dog.say()
dog.eat()
复制代码
Set 和 Map
Set 和 Map 都是 ES6 中新增的数据结构,是对当前 JS 数组和对象这两种重要数据结构的扩展。
- Set类似于数组,不过数组内部可以有重复的元素,但Set内不可以有重复的元素,会自动过滤掉
- Map类似于对象,不过普通的对象的key必须是字符串或者数字,而新增的Map中的key可以是任何数据类型。
- Set实例不允许元素有重复的,即使你通过它添加元素的方法往内部添加元素也会被去重掉
- Set实例的5个 属性和方法
size:获取元素的数量
add(value):添加元素到数组中,返回Set实例本身
delete(value):删除数组中的某元素,返回Boolean值表示删除是否成功
has(value):返回Boolean值,表示该值是否是Set实例的元素
clear():清除所有元素,没有返回值
复制代码
- Set实例的遍历:
keys():返回键名的遍历器
values():返回值的遍历器,因为Set结构没有键名,只有键值,所以keys()和value()返回的结果一样
entries():返回键值对的遍历器
forEach():使用回调函数遍历每个成员
复制代码
- Map
map.set(obj, ‘OK’) 就是用对象作为的 key (不光可以是对象,任何数据类型都可以)
- Map实例6个 属性和方法:
size:获取成员数量
set:设置key和value
get:获取成员的属性值
has:判断成员是否存在
delete:删除成员
clear:清空所有
复制代码
- Map 实例的遍历方法有:
keys() :返回键名的遍历器。
values() :返回键值的遍历器。
entries() :返回所有成员的遍历器。
forEach() :遍历 Map 的所有成员。
复制代码
Promise
Promise 是 CommonJS 提出来的这一种规范,Promise 可以将回调变成链式调用写法,流程更加清晰,代码更加优雅。
简单归纳下 Promise:三个状态、两个过程、一个方法
三个状态: pending 、 fulfilled 、 rejected
两个过程:
pending→fulfilled(resolve)
pending→rejected(reject)
一个方法: then
当然还有其他概念,如 catch 、 Promise.all/race ,这里就不展开讲了。
复制代码
总结
本篇文章总结了ES基础语法中一些经常考察的知识点,这些基础知识点作为前端都是要掌握的,而且我这边的总结都写的比较浅,感兴趣的同学可以深入去学习一下