梳理一下基础知识:

1. JS数据类型

js数据类型分为:基础数据类型(7种)和引用数据类型object。
基础数据类型: null、undefined、 number、 string、 boolean 、symbol(es6)、 bigint(可以表示大于2^53 -1的整数)
引用数据类型:object。object包括array、function、Date、RegExp等

2. 判断数据类型的方式:

(1) typeof:
用来判断除了null的基本数据类型,typeof null返回object.
引用数据类型除function外,都返回object

var a = 1;
var b = 'lala';
var c = true;
var d = undefined;
var e = Symbol();
var f = 1n;var g = null;
var h = {};
var i = [];
var j = function() {};console.log(typeof a);  // number
console.log(typeof b);  // string
console.log(typeof c);  // boolean
console.log(typeof d);  // undefined
console.log(typeof e);  // symbol
console.log(typeof f);  // bigintconsole.log(typeof g);  // object
console.log(typeof h);  // object
console.log(typeof i);  // object
console.log(typeof j);  // function

(2)instanceof
用来判断引用数据类型,类实例,instanceof会沿着原型链一直往上寻找,返回值为true或false

var h = {};
var i = [];
var j = function() {};
var k = new Date();
var l = /[0-9]/g;console.log(h instanceof Object);  // trye
console.log(i instanceof Array);  // true
console.log(j instanceof Function);  // true
console.log(k instanceof Date);  // true
console.log(l instanceof RegExp);  // true

(3) Object.prototype.toString.call()

Object.prototype.toString.call(null); // "[object Null]"
Object.prototype.toString.call(undefined); // "[object Undefined]"
Object.prototype.toString.call('lala');// "[object String]"
Object.prototype.toString.call(1);// "[object Number]"
Object.prototype.toString.call(true);// "[object Boolean]"
Object.prototype.toString.call(new Date());// "[object Date]"
Object.prototype.toString.call(/[0-9]/);// "[object RegExp]"

手写instanceof

function myInstanceof(left, right) {// 这里先用typeof来判断基础数据类型,如果是,直接返回falseif(typeof left !== 'object' || left === null) return false;// getProtypeOf是Object对象自带的API,能够拿到参数的原型对象let proto = Object.getPrototypeOf(left);while(true) {                  //循环往下寻找,直到找到相同的原型对象if(proto === null) return false;if(proto === right.prototype) return true;//找到相同原型对象,返回trueproto = Object.getPrototypeof(proto);}
}// 验证一下自己实现的myInstanceof是否OK
console.log(myInstanceof(new Number(123), Number));    // true
console.log(myInstanceof(123, Number));

3. 深浅拷贝

这要从数据的在内存中存储的位置说起,基本数据类型存储在栈中,引用数据类型数据存储在堆中,栈中存储了访问对象的地址。

对于基础数据类型来讲,都是对值的拷贝;
但是对引用数据类型来讲,分为深拷贝和浅拷贝。

浅拷贝首先会创建一个对象,对原对象的属性值精准拷贝,如果属性值是基础数据类型,拷贝的就是基础数据类型的值,如果属性值是引用数据类型,拷贝的是内存地址。

深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。也就是说是在堆内存中重新开辟空间,拷贝后数据存放在新的地址,同时指针指向新的地址,与原数据完全隔离。

常见的浅拷贝的实现方式有:
object.assign(target, …sources)
扩展运算符 …
concat 拷贝数组
slice 拷贝数组 arr.slice(begin, end);

// Object.assign()
let target = { a: 1 }
let source = { b: 2, c: { d: 3 } };Object.assign(target, source);
console.log(target);  // { a: 1, b: 2, c: { d: 3 } };target.b = 5;
target.c.d = 4;
console.log(source); // { b: 2, c: { d: 4 } };
console.log(target); // { a: 1, b: 5, c: { d: 4 } };// ...
let obj = { a:1, b: { c:1 } }
let obj2 = { ...obj }
let arr = [1, 2, 3];
let newArr = [...arr];// concat()
let arr = [1, 2, 3];
let newArr = arr.concat();// slice()
let arr = [1, 2, {val: 4}];
let newArr = arr.slice();

但是使用 object.assign 方法有几点需要注意:

  • 它不会拷贝对象的继承属性;
  • 它不会拷贝对象的不可枚举的属性;
  • 可以拷贝 Symbol 类型的属性。
let obj1 = { a:{ b:1 }, sym:Symbol(1)};
Object.defineProperty(obj1, 'innumerable' ,{value:'不可枚举属性',enumerable:false
});
let obj2 = {};
Object.assign(obj2,obj1)
obj1.a.b = 2;
console.log('obj1',obj1);  // { a: { b: 2 }, sym: Symbol(1), innumerable: '不可枚举属性'}
console.log('obj2',obj2);  // { a: { b: 2 }, sym: Symbol(1)}

深拷贝的实现方式:JSON.stringify

但是使用 JSON.stringify 实现深拷贝还是有一些地方值得注意,总结下来主要有这几点:

  1. 拷贝的对象的值中如果有函数、undefined、symbol 这几种类型,经过 JSON.stringify 序列化之后的字符串中这个键值对会消失;
  2. 拷贝 Date 引用类型会变成字符串;
  3. 无法拷贝不可枚举的属性;
  4. 无法拷贝对象的原型链;
  5. 拷贝 RegExp 引用类型会变成空对象;
  6. 对象中含有 NaN、Infinity 以及 -Infinity,JSON 序列化的结果会变成 null;
  7. 无法拷贝对象的循环应用,即对象成环 (obj[key] = obj)。
function Obj() { this.func = function () { alert(1) }; this.obj = {a:1};this.arr = [1,2,3];this.und = undefined; this.reg = /123/; this.date = new Date(0); this.NaN = NaN;this.infinity = Infinity;this.sym = Symbol(1);
}
let obj1 = new Obj();
Object.defineProperty(obj1,'innumerable',{ enumerable:false,value:'innumerable'
});
console.log('obj1',obj1);
let str = JSON.stringify(obj1);
let obj2 = JSON.parse(str);
console.log('obj2',obj2);

手写浅拷贝

const shallowClone = (target) => {if (typeof target === 'object' && target !== null) {let cloneTarget = Array.isArray(target) ? [] : {};for(let prop in target) {if (target.hasOwnProperty(prop)) {  // 遍历对象自身可枚举属性(不考虑继承属性和原型对象cloneTarget[prop] = target[prop]}}return cloneTarget} else {return target}
}

手写深拷贝

const deepClone = (target) => {if (typeof target === 'object' && target !== null) {let cloneTarget = Array.isArray(target) ? [] : {};for(let prop in target) {if (target.hasOwnProperty(prop)) {  // 遍历对象自身可枚举属性(不考虑继承属性和原型对象cloneTarget[prop] = deepClone(target[prop]);  //递归导致日期、正则变成{}}}return cloneTarget} else {return target}
}

改进版深拷贝

考虑日期、正则、循环引用

const deepClone = (target, map = new WeakMap()) => {if (target === null) return nullif (typeof target !== 'object' || target.constructor === Date || target.constructor === RegExp) return targetif (map.has(target)) return target  // 解决循环引用const deepTarget = Array.isArray(target) ? [] : {};map.set(target, true)for (let prop in target) {if (target.hasOwnProperty(prop)) {  // 遍历对象自身可枚举属性(不考虑继承属性和原型对象deepTarget[prop] = deepClone(target[prop], map)}}return deepTarget
}

4. 原型

原型对象

首先思考三个问题:

  1. 什么是原型对象?
  2. 原型对象何时产生?
  3. 原型对象如何访问?

什么是原型对象?

原型对象本质就是一个对象,所有函数都有prototype属性,该属性指向函数的原型对象,原型对象中包括:

  • constructor: 指向构造函数
  • 继承自Object的属性和方法

原型对象何时产生?

原型对象在函数创建的时候产生,每声明一个函数,都会做以下操作:

  • 浏览器会在内存中创建一个对象
  • 对象中添加一个constructor属性
  • constructor属性指向该函数
  • 将新创建的对象赋值给函数的prototype属性

原型对象如何访问?

函数名.prototype
通过函数实例的__proto__属性,函数每创建一个实例,该实例内部包含一个指针,指向构造函数的原型对象。

5. 原型链

原型链就是访问实例上某个属性或者方法时,先在实例中查找,找到即返回;若没有,则通过__proto__属性到构造函数的原型对象prototype上去找,找到则返回;若没有,则继续到构造函数的原型对象prototype的__proto__属性查找,直到搜索到Object.prototype为止。

一张图理解原型、原型链

总结

所有函数都是Function的实例,Function也是Function的实例
Function继承Object,除Object外,其他一切皆继承自Object

6. 继承

有两个函数、函数A、函数B,实现函数A继承函数B的属性和方法:

原型链继承

A.prototype = new B()
A.prototype.constructor = Avar a1 = new A()
var a2 = new A()

缺点:

  1. 父类B函数原型对象的引用类型属性会被实例 a1 a2共享
  2. 创建子类A的实例无法向父类B传参数
  3. 子类无法实现继承多个函数,这里A只能继承自B

构造函数继承

function A(e) {B.call(this, e)
}

缺点:
子类无法访问父类原型上的属性和方法

组合继承

function B (name, age) {this.name = namethis.age = age
}
B.prototype.setName = function (name) {this.name = name
}function A (name, age, price) {B.call(this, name, age)this.price = price
}A.prototype = new B ()
A.prototype.constructor = AA.prototype.setPrice = function (price) {this.price = price
}var sub = new A()

缺点:
调用了两次B():一次是B.call();一次是new B()

寄生组合继承

Object.create 方法,这个方法接收两个参数:一是用作新对象原型的对象、二是为新对象定义额外属性的对象(可选参数)。

function B (name, age) {this.name = namethis.age = age
}
B.prototype.setName = function (name) {this.name = name
}
t
function A (name, age, price) {B.call(this, name, age)this.price = price
}// 第一种写法
// 创建一个对象{},并且把对象的_proto_赋值为Object.create 的参数
// A.prototype.__proto__ = B.prototype
A.prototype = Object.create(B.prototype, {consturctor: A})// 第二种写法
//var F = function () { } //核心代码
//F.prototype = B.prototype; //核心代码
//A.prototype = new F();
//A.prototype.contructor = AA.prototype.setPrice = function (price) {this.price = price
}var sub = new A()

ES6 Class类

class B {static mood = 'good'  // 静态属性constructor () {this.money = 1000000}buybuybuy () {this.money -= 100console.log('money', this.money)}
}class A extends B {super()
}    var a1 = new A()
a1.buybuybuy()

7. 执行上下文(Execution Context)

浏览器获取到源代码后,主要做了几个事情:

  • 分词/词法分析():将代码进行分割,生成token;
  • 解析/语法分析():按照语法将token转换成AST抽象语法树;
  • 可执行代码:解析器生成字节码,逐行解释执行,分析器监控热点代码,编译器将热点代码编译为机器码。

什么是执行上下文?

执行上下文,又称执行上下文环境。执行上下文分为三种类型:

  • 全局执行上下文:程序开始时,会创建全局执行上下文,并压入执行栈中。
  • 函数执行上下文:当函数被调用时创建函数执行上下文,并将函数压入执行栈中。
  • eval执行上下文:eval函数专有的执行上下文。

执行上下文分两个阶段:创建阶段和执行阶段。

创建阶段

执行上下文主要由两部分组成:词法环境和变量环境。

词法环境(LexicalEnvironment)

词法环境分类:全局、函数、模块

词法环境构成:

  • 环境记录(Environment Record):存放、初始化变量

     声明式环境记录(Declarative Environment Record): 存放直接用标识符定义的元素,比如const let声明的变量对象式环境记录(Object Environment Record):主要用于with的语法环境。
    
  • 外部环境(Outer):创建作用域链,访问父词法作用域的引用

  • thisBinding:确定当前环境中this的指向

变量环境(variableEnvironment)

也是一个词法环境。主要的区别在于通过var 声明的变量以及函数声明存放在变量环境。

简单来说,执行上下文创建阶段主要做了三件事情:

  1. 初始化变量、函数、形参
  2. 创建作用域链
  3. 绑定this
executionContext = {variableObject: {arguments: {},name: undefined,getData: undefined},  // 初始化变量、函数、形参scopeChain: {},  // 创建作用域链this: {} // 绑定this
}

执行阶段

执行阶段主要做了两件事:

  1. 分配变量、函数的引用、赋值
  2. 执行代码
    变量提升 vs 暂时性死区
    var声明的变量以及函数声明,在执行上下文创建阶段,已经初始化完成,并赋值为undefined,代码未执行到var赋值行,也可以访问var定义的变量,值为undefined,这种现象被称作变量提升

相反,由const、let声明的变量,在词法环境中,初始化时会被置为标志位,在代码没执行到let、const赋值行时,提前读取变量会报错,这个特性叫做暂时性死区。

执行上下文栈

浏览器的JS解释器是单线程的,相当于浏览器在同一时间只能做一件事。
代码中只有一个全局执行上下文,和无数个函数执行上下文,组成了执行上下文栈。
一个函数的执行上下文,在函数执行完毕后会被移除执行栈。

8. 作用域

作用域的主要用途是隔离变量和函数,并控制它们的生命周期。主要分为三种类型:

  • 全局作用域
  • 函数作用域
  • 块级作用域

作用域是在执行上下文创建时定义的,不是在代码执行时创建的,因此又称为词法作用域。

词法作用域 vs 动态作用域

词法作用域语动态作用域的区别是,词法作用域是执行上下文创建阶段阶段就定义的,动态作用域是指代码执行阶段创建的。
为了更好的理解JS采用的是词法作用域,看一下例子:

var name = 'xuna'
function getName() {console.log(name)
}
function getName1() {var name = 'na.xu'return getName()
}
getName1() // xuna

作用域链

当一个函数嵌套另一个函数时,在当前执行上下文环境的词法环境和变量环境的环境记录(Environment Record)中无法找到某个变量,就会通过外部环境(Outer)去访问父词法作用域,如果还没找到,就一层一层向上寻找,直到找到该变量或抵达全局作用域为止,这样的链式关系称为作用域链。

9. 闭包

闭包一般发生在函数嵌套时,内部函数访问外部函数的变量。
高级程序设计三中:闭包是指有权访问另一个函数作用域中的变量的函数,可以理解为(能够读取其他函数内部变量的函数)
再理解一下:
一个函数执行完,被弹出执行栈,当前执行上下文中不能直接访问被弹出栈的函数的词法作用域,而另一个函数中还保留了对该函数词法作用域的引用,这个引用就是闭包。

闭包的应用

封装私有变量
function Person() {var money = 10000return buy() {money -= 1}
}var person = new Person()
person.buy() // money为person的私有变量,只能通过buy()修改
缓存数据
function getDataList() {let data = nullreturn {getData() {if(data) return Promise.resolve(data)return fetch().then(res => data = res.json())}}
}const list = getDataList()
list.getData()
柯里化

JS内功修炼-基础篇相关推荐

  1. 开发内功修炼网络篇电子书出炉!!!

    点击上方蓝字"开发内功修炼",关注并设为星标 了解你的每一比特,用好你的每一纳秒 飞哥的开发内功修炼技术号更新文章有一年多了,以前的文章都是单独介绍一个技术点,没给大家一个整体的视 ...

  2. Three.js(2)--->基础篇-Helpers(辅助对象/辅助线)

    在Three.js中有许多的Helper(辅助对象)用来帮助我们的开发. 本篇例举几个常见的,方便理解.以及一些效果 文章目录 前言 一.AxesHelper 二.BoxHelper 三.Box3He ...

  3. 【知识点总结】内功修炼-算法篇

    [知识点总结]内功修炼-算法篇 五大基本特征 设计要求 排序算法 1.选择排序 2.冒泡排序 3.插入排序 时间复杂度总汇 排序算法 概念 分治思想 递归 知识点 前缀表达式 中缀表达式 后缀表达式 ...

  4. Node.js后端开发 - 基础篇 #18 nodemon工具

    文章目录 前言 nodemon工具简单介绍 nodemon工具安装 nodemon工具使用(node app.nodemon app) nodemon工具使用(npm run start) 前言 上篇 ...

  5. 开发内功修炼内存篇汇总

    最近终于抽空把内存篇更新完了,我分享出来的这些问题,其实都是我之前没有搞的特别明白的困惑.刚刚开始的时候,可能就是在我脑子里蹦出的一个疑问,比如内存随机IO会比顺序IO慢吗? 为了解开这个疑惑,我曾经 ...

  6. 【android免root脚本制作】总览Auto.js开发小结——基础篇

    Auto.js是什么 Auto.js是一款写脚本,ui界面,运行脚本,制作简单安卓app的一体式软件.并且是全开源的免费APP,类似于按键精灵,而且本软件有全部按键精灵的功能,还有其他例如控件操作等, ...

  7. JS服务器端开发基础篇(Array.slice方法和splice方法)

    Array.slice方法和splice方法在众多的JS数组中属于比较复杂的一个方法,而且容易记混.搜索网络上很多资料都没有发现系统的总结.特别归纳如下,不完全处还希望各位批评指正.  一.slice ...

  8. js页面排序-----基础篇

    由于客户查询出来的结果集比较多,一页就有五百条,所以客户希望单行表格的列头的时候可以根据这一页在本页排序,既然是只涉及到页面的排序,则不需去和服务器交互,直接利用js把结果集表格重排一下即可. 在网上 ...

  9. 3.3.1JavaScript网页编程——WebAPI(JS之DOM基础篇,含事件)

    目录 DOM和BOM DOM 根据CSS选择器来获取DOM元素 (重点) 选择引号里面加**css选择器** 获取页面标签querySelect和querySelectAll 其他获取DOM元素方法( ...

  10. JS基础篇--HTML DOM classList 属性

    页面DOM里的每个节点上都有一个classList对象,程序员可以使用里面的方法新增.删除.修改节点上的CSS类.使用classList,程序员还可以用它来判断某个节点是否被赋予了某个CSS类. 添加 ...

最新文章

  1. atitit.人脸识别的应用场景and使用最佳实践 java .net php
  2. ElasticSearch和mongodb的对比
  3. shell date cal
  4. 信息论基础(学习笔记整理)
  5. 基于抛物线过渡(梯形加减速)的空间直线插补算法与空间圆弧插补算法(Matlab)
  6. java 支付宝转账_Java 支付宝支付,退款,单笔转账到支付宝账户(单笔转账到支付宝账户)...
  7. html画布刮刮乐,h5canvas实现刮刮乐效果的方法
  8. wps怎么把xlsx转成html,怎样把wps转换成excel
  9. vuejs中使用vuex的两种方案之一
  10. 用python画微笑脸表情_“裂开了,苦涩了,翻白眼”!我用Python画出微信新出的表情包...
  11. python 矩阵和三角函数
  12. flask android app socketio加解密 匿名加密聊天室 不被任何官方非官方机构个人监视的匿名聊天室!!! 想聊什么就聊什么!
  13. 12、乐趣国学—践行《弟子规》的“信”懂得处世之道(下篇)
  14. 阐述HTML语言的基本语法规则,信息组织学》考试试卷(A)试卷(一)
  15. 【附源码例】快捷指令实现调出iOS隐藏应用程序-原理解析
  16. html标题如何设置行书,四招打通行书之“气”
  17. 计网课设 模拟实验拓扑
  18. 定理在数学中的简写形式_数学中的s代表着什么? s符号在数学中表示什么
  19. 管理经济学【八】之 完全竞争市场中的企业决策
  20. 在WSL中使用Linux桌面环境的尝试与总结

热门文章

  1. survival cutoff值
  2. 微信小程序对商户而言到底有什么用?
  3. 爬虫笔记(二)——Beautiful Soup库
  4. 嵌入式网络基础知识——MQTT引入
  5. canvas漫天闪烁的星星
  6. 2016杭州云栖大会随笔
  7. 详解 python 的 切片
  8. C语言系列(5) --- C语言文件的操作
  9. 【金猿产品展】EasyTwin——国产自研数字孪生融合渲染引擎
  10. iWebShop 电商项目实战003----页面初始化数据测试