一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第2天,点击查看活动详情。
往期文章
前言
一、一年一度的金(tong)三银(tie)四,不知各位胸怀大志的小伙伴们是否找到了自己心仪的工作呢
二、下面我对JavaScript中的部分基础知识做了一些梳理和总结,希望各位掘(da)友(lao)们能够温(shou)故(xia)知(liu)新(qing)
总结
1.数据类型
一、种类
1、原始数据类型:
- Number、String、Boolean、Undefined、null、symbol(es6)、BigInt(es10)
2、引用数据类型:
- Object、Array、Function,包括Date、RegExp、Map、Set等
二、区别
1、原始数据类型:
- 直接存储在栈(stack)中,占据空间小、大小固定,属于被频繁使用数据
2、引用数据类型:
- 同时存储在栈(stack)和堆(heap)中,占据空间大、大小不固定
- 在栈中存储了指针,该指针指向堆中该实体的起始地址
三、小结
1、声明变量时不同的内存地址分配:
- 简单类型的值存放在栈中,在栈中存放的是对应的值
- 引用类型对应的值存储在堆中,在栈中存放的是指向堆内存的地址
2、不同的类型数据导致赋值变量时的不同:
- 简单类型赋值,是生成相同的值,两个对象对应不同的地址
- 复杂类型赋值,是将保存对象的内存地址赋值给另一个变量,也就是两个变量指向堆内存中同一个对象
2.类型检测
一、方法
1、typeof:
- 返回一个字符串,表示未经计算的操作数的类型
- 适合对基本类型及 function 的检测使用,对引用数据类型等不适合使用
typeof undefined; // "undefined"
typeof null; // "object"
typeof 100; // "number"
typeof NaN; // "number"
typeof true; // "boolean"
typeof 'foo'; // "string"
typeof function () {}; // "function"
typeof [1, 2]; // "object"
typeof new Object(); // "object"
2、instanceof:
- 用于检测一个对象在其原型链中是否存在一个构造函数的 prototype 属性
- 适合用于判断对象是否属于 Array、Date 和 RegExp 等内置对象
100 instanceof Number; // false
true instanceof Boolean; // false
'foo' instanceof String; // false
function () {} instanceof Function; // true
[1, 2] instanceof Array; // true
new Object() instanceof Object; // true
- 任何一个构造函数都有一个 prototype 对象属性,对象属性将用作 new 实例化对象的原型对象
function Person() {}
function Student() {}
Student.prototype = new Person();
Student.prototype.constructor = Student;
const ben = new Student();
ben instanceof Student; // true
const one = new Person();
one instanceof Person; // true
one instanceof Student; // false
ben instanceof Person; // true
3、Object.prototype.toString:
- 通过 toString() 来获取每个对象的类型
- 能精准地判断出值的数据类型
Obejct.prototype.toString.call(undefined); // "[object Undefined]"
Obejct.prototype.toString.call(null); // "[object Null]"
Obejct.prototype.toString.call(true); // "[object Boolean]"
Obejct.prototype.toString.call(''); // "[object String]"
Obejct.prototype.toString.call(123); // "[object Number]"
Obejct.prototype.toString.call([]); // "[object Array]"
Obejct.prototype.toString.call({}); // "[object Object]"
4、constructor
(100).constructor === Number; // true
(true).constructor === Boolean; // true
('foo').constructor === String; // true
(function () {}).constructor === Function; // true
([1, 2]).constructor === Array; // true
(new Object()).constructor === Object; // true
5、数组检测:
- 准确检测一个变量是否为数组类型
Array.isArray(variable);
3.类型转换
一、规则
1、ToString:
String(null); // "null"
String(undefined); // 'undefined'
String(true); // 'true'
String(10); // '10'
String([]); // ''
String({}); // '[object Object]'
2、ToNumber:
Number(null); // 0
Number(undefined); // NaN
Number('10'); // 10
Number(true); // 1
Number([]); // 0
Number({}); // NaN
3、ToBoolean:
- JavaScript 中假值只有
false
、null
、undefined
、""
、0
和NaN
,其他值转为 Boolean 类型都为true
二、显示转换
1、转换为数值类型:
- Number(mix)、parseInt(string, radix)、parseFloat(string)
2、转换为字符串类型:
- toString(radix)、String(mix)
3、转换为布尔类型:
- Boolean(mix)
三、隐式转换
1、当运算符在运算时,如果 两边数据不统一,CPU 就无法运算,这时我们编译器会自动将运算符两边的数据做一个数据类型转换,转成相同的数据类型再计算:
- 这种无需开发者手动转换,而由 编译器自动转换 的方式就称为 隐式类型转换
2、数据类型隐式转换主要分为三种情况:
- 转换为 Boolean 类型、转换为 Number 类型、转换为 String 类型
四、运算符
1、加号运算符:
- 当加号运算符作为一元运算符运算值时,它会将该值转换为 Number 类型
' ' + // 0
'String' + // NaN
true + // 1
undefined + // 0
null + // 0
[] + // 0
{} + // NaN
- 当加号运算符作为二元运算符操作值时,它会根据两边值类型进行数据类型隐式转换
"1" + 1 // "11"
"1" + "1" // "11"
"1" + true // "1true"
"1" + NaN // "NaN"
"1" + [] // "1"
"1" + {} // "1[object Object]"
1 + "true" // "1true"
1 + true // 2
1 + undefined // NaN
1 + null // 1
1 + [] // "1"
1 + {} // "1[object Object]"
2、相等运算符:
- 相等运算符
==
会对操作值进行隐式转换后进行比较
'1' == true; // true
'1' == 1; // true
'1' == {}; // false
'1' == []; // false
undefined == undefined; // true
undefined == null; // true
nul == null; // true
3、关系运算符:
- 会把其他数据类型转换成 Number 之后再比较关系(除了 Date 类型对象)
5 > 10; // false
'2' > 10; // false
'2' > '10'; // true
'abc' > 'b'; // false
'abc' > 'aad'; // true
4、NaN 不和任何类型的值相等,包括它自己,它与任何类型的值比较大小时都返回 false
4.this
一、定义
this
对象是是执行上下文中的一个属性,它指向最后一次调用这个方法的对象- 在全局函数中,
this
等于window
,而当函数被作为某个对象调用时,this等于那个对象
二、判断
1、函数调用:
- 当一个函数不是一个对象的属性时,直接作为函数来调用时,
this
指向全局对象
2、方法调用:
- 如果一个函数作为一个对象的方法来调用时,
this
指向这个对象
3、构造函数调用:
this
指向这个用new
新创建的对象
4、apply 、 call 和 bind
调用模式:
- 这三个方法都可以显示的指定调用函数的 this 指向
apply
接收参数的是数组,call
接受参数列表,bind
方法通过传入一个对象,返回一个this
绑定了传入对象的新函数- 这个函数的
this
指向除了使用new
时会被改变,其他情况下都不会改变
5.闭包
一、定义
- 指有权访问另一个函数作用域中的变量的函数
- 在一个函数内创建另一个函数,创建的函数可以访问到当前函数的局部变量
二、作用
- 访问函数内部变量、保持函数在环境中一直存在,不会被垃圾回收机制处理
三、应用场景
- 能够访问函数定义时所在的词法作用域(阻止其被回收)
- 私有化变量
- 模拟块级作用域
- 创建模块
四、优缺点
- 优点:能够让希望一个变量长期驻扎在内存之中成为可能,避免全局变量的污染,以及允许私有成员的存在
- 缺点:就是常驻内存会增大内存使用量,并且使用不当容易造成内存泄漏
6.作用域 作用域链
一、作用域
1、定义:
- 作用域是定义变量的区域,它有一套访问变量的规则
- 这套规则来管理浏览器引擎如何在当前作用域以及嵌套的作用域中根据变量(标识符)进行变量查找
2、工作模式:
- 词法作用域(静态作用域):它的定义过程发生在代码的书写阶段。词法作用域是在 定义 时确定的
- 动态作用域:不关心函数和作用域是如何声明以及在何处声明,它只关心它们从何处调用。动态作用域是在 运行 时确定的
3、类型:
- 全局作用域:任何不在函数中或是大括号中声明的变量,都是在全局作用域下,全局作用域下声明的变量可以在程序的任意位置访问
- 函数作用域:属于这个函数的全部变量都可以在整个函数的范围内使用及复用
- 块级作用域:任何一对花括号中的语句集都属于一个块,在这之中定义的所有变量在代码块外都是不可见的
二、作用域链
1、定义:
- 当代码在一个环境中执行时,会创建变量对象的一个作用域链
- 通过作用域链,我们可以访问到外层环境的变量和函数
2、作用:
- 作用域链的作用是保证对执行环境有权访问的所有变量和函数的有序访问
- 如果在该变量对象中没有找到对应变量或函数,则会根据执行作用域链向上继续查找
7.变量提升
一、运行阶段
- 预编译阶段:读取 变量的定义 并 确定其作用域 即生效范围
- 执行阶段
二、变量定义
- 使用
var
或let
关键字定义的变量,在未赋值的情况下,该变量的值是undefined
- 使用
const
关键字定义变量却不赋值,将会抛出错误
三、变量作用域
- 全局变量的作用域遍布全局
- 局部变量的作用域仅在于函数内部及其嵌套函数的作用域
- 函数内部的同名变量或参数优先级高于全局同名变量
四、变量声明提升
- 只提升声明部分(未赋值状态),赋值部分保持原位置不动
五、函数声明提升
- 会将函数的声明和定义全都提升至作用域顶部
六、函数覆盖
- 函数声明和变量声明都会被提升。但是,函数声明会覆盖变量声明
- 如果变量存在赋值操作,则最终的值为变量的值
- 变量的重复声明是无用的,但 函数的重复声明会覆盖前面的声明(无论是变量还是函数声明)
8.new
一、作用
1、用于创建一个给定构造函数的实例对象:
new
通过构造函数Person
创建出来的实例可以访问到构造函数中的属性new
通过构造函数Person
创建出来的实例可以访问到构造函数原型链中的属性(即实例与构造函数通过原型链连接了起来)
二、流程
1、主要做了以下的工作:
- 创建一个新的对象
obj
- 将对象与构建函数通过原型链连接起来
- 将构建函数中的
this
绑定到新建的对象obj
上 - 根据构建函数返回类型作判断,如果是原始值则被忽略,如果是返回对象,需要正常处理
9.call apply bind
一、定义
- 作用是改变函数执行时的上下文,简而言之就是改变函数运行时的
this
指向
二、作用
1、apply:
apply
接受两个参数,第一个参数是this
的指向,第二个参数是函数接受的参数,以数组的形式传入- 改变
this
指向后原函数会立即执行,且此方法只是临时改变this
指向一次 - 当第一个参数为
null
、undefined
的时候,默认指向window
(在浏览器中)
2、call:
call
方法的第一个参数也是this
的指向,后面传入的是一个参数列表- 跟
apply
一样,改变this
指向后原函数会立即执行,且此方法只是临时改变this
指向一次 - 同样的,当第一个参数为
null
、undefined
的时候,默认指向window
(在浏览器中)
3、bind:
- bind方法和call很相似,第一参数也是
this
的指向,后面传入的也是一个参数列表(但是这个参数列表可以分多次传入) - 改变
this
指向后不会立即执行,而是返回一个永久改变this
指向的函数
三、区别:
- 三者都可以改变函数的
this
对象指向 - 三者第一个参数都是
this
要指向的对象,如果如果没有这个参数或参数为undefined
或null
,则默认指向全局window
- 三者都可以传参,但是
apply
是数组,而call
是参数列表,且apply
和call
是一次性传入参数,而bind
可以分为多次传入 bind
是返回绑定this之后的函数,apply
、call
则是立即执行
10.原型 原型链
一、原型
- 对象中固有的
__proto__
属性,该属性指向对象的prototype
原型属性
二、原型链
- 当我们访问一个对象的属性时,如果这个对象内部不存在这个属性,那么它就会去它的原型对象里找这个属性
- 这个原型对象又会有自己的原型,于是就这样一直找下去
- 原型链的尽头一般来说都是
Object.prototype
,所以这就是我们新建的对象为什么能够使用toString()
等方法的原因
三、小结
JavaScript
对象是通过引用来传递的,我们创建的每个新对象实体中并没有一份属于自己的原型副本- 当我们修改原型时,与之相关的对象也会继承这一改变
结尾
- 好了,本篇的所有内容就介绍到这里了,希望大家能够在未来的日子里继续加油!