javascript
# 7 种原始类型和 1 种引用类型
number
任何类型的数字(原始类型)bigint
任意长度的整数 (原始类型)尾部的 "n" 表示这是一个 BigInt 类型string
字符串 (原始类型)boolean
true 和 false (原始类型)null
未知的值 (原始类型)undefined
未定义的值 (原始类型)symbol
唯一的标识符 (原始类型)object
更复杂的数据结构(引用类型)
原始类型和引用类型区别:
JS 中,所有的变量都是保存在栈内存中的。
基本数据类型:
基本数据类型的值,直接保存在栈内存中。值与值之间是独立存在,修改一个变量不会影响其他的变量。
引用数据类型:
对象是保存到堆内存中的。每创建一个新的对象,就会在堆内存中开辟出一个新的空间;而变量保存了对象的内存地址(对象的引用),保存在栈内存当中。如果两个变量保存了同一个对象的引用,当一个通过一个变量修改属性时,另一个也会受到影响。
# null 和 undefined 区别
- null 专门用来定义一个空对象(例如:
let a = null
)。如果你想定义一个变量用来保存引用类型,但是还没想好放什么内容,这个时候,可以在初始化时将其设置为 null。 - undefined:变量已声明未赋值时、变量未声明(未定义)时、函数无返回值时、调用函数时,未传参
- 任何数据类型和 undefined 运算都是 NaN;(10 + null = 10)
- 任何值和 null 运算,null 可看做 0 运算。(10 + undefined = NaN)
# 判断数据类型
- typeof(返回值是 string 类型)
typeof 100; // "number"
typeof 10n; // "bigint"
typeof "jason"; // "string"
typeof true; // "boolean"
typeof undefined; // "undefined"
typeof Symbol("id"); // "symbol"
typeof null; // "object" 这是 JavaScript 编程语言的一个错误,实际上它并不是一个 object
typeof console; // "function"
- instanceof
- Object.prototype.toString.call()
- constructor
# 类型转换
三种情况:字符串转换、数字型转换、布尔转换
# 运算符
加法 +,减法 -,乘法 *,除法 /,取余 %,求幂 **
console.log(6 + 2); // 8
console.log(6 - 2); // 4
console.log(6 * 2); // 12
console.log(6 / 2); // 3
console.log(6 % 4); // 2
console.log(6 ** 2); // 36
var a = 1,
b = 1;
console.log(++a); // 2,前置运算符返回最新值
console.log(b++); // 1,后置运算符返回旧值
// 转布尔
console.log(Boolean(undefined)); // false
console.log(Boolean(null)); // // false
// 转数字
console.log(+undefined); // NaN
console.log(+null); // // 0
// 逗号运算符ti
let a = (1 + 2, 3 + 4); // 每个语句都运行,只有最后的语句的结果会被返回
console.log(a); // 7(3 + 4 的结果)
提示
做运算的时候,把运算元(参数)转换成数字再继续运算
# 值的比较
- 大于 / 小于:a > b,a < b。
- 大于等于 / 小于等于:a >= b,a <= b。
- 检查两个值的相等:a == b,请注意双等号 == 表示相等性检查,而单等号 a = b 表示赋值。
- 检查两个值不相等。不相等在数学中的符号是 ≠,但在 JavaScript 中写成 a != b。
console.log(undefined == null); // true
console.log(undefined === null); // false
console.log(!undefined === !null); // true 一元运算符比===优先级高
console.log(+undefined === +null); // false 一元运算符比===优先级高
# 面向对象
# 对象的创建&构造函数
- 方法一:对象字面量
var obj = {};
- 方法二:对象字面量
/*
* 使用工厂方法创建对象
* 通过该方法可以大批量的创建对象
*/
function createPerson(name, age, gender) {
//创建一个新的对象
var obj = new Object();
//向对象中添加属性
obj.name = name;
obj.age = age;
obj.gender = gender;
obj.sayName = function() {
alert(this.name);
};
//将新的对象返回
return obj;
}
var obj2 = createPerson("lee", 28, "男");
var obj3 = createPerson("rui", 27, "男");
var obj4 = createPerson("gna", 18, "女");
- 方法三:利用构造函数
// 创建一个构造函数
function Student(name) {
this.name = name; //this指的是当前对象实例
this.sayHi = function() {
console.log(this.name);
};
}
//利用构造函数自定义对象
var stu1 = new Student("jason");
console.log(stu1);
var stu2 = new Student("lrg");
console.log(stu2);
构造函数:是一种特殊的函数,主要用来创建和初始化对象,也就是为对象的成员变量赋初始值。它与 new
一起使用才有意义。构造函数的创建方式和普通函数没有区别,不同的是构造函数习惯上首字母大写。
# new 命令
new 命令的作用:就是执行构造函数,返回一个实例对象。
- 创建一个空对象,作为将要返回的对象实例。
- 将这个空对象的原型,指向构造函数的 prototype 属性。
- 将这个空对象赋值给函数内部的 this 关键字。
- 开始执行构造函数内部的代码。
function _new(constructor, ...rest) {
var context = Object.create(constructor.prototype); //
var result = constructor.apply(context, rest);
var isObj = typeof result === "object" && result !== null;
return isObj ? result : context;
}
// 测试
function Person(name) {
this.name = name;
}
var person = _new(Person, "jason");
console.log(person.name);
new.target: 函数内部可以使用 new.target 属性。如果当前函数是 new 命令调用,new.target 指向当前函数,否则为 undefined
function fn() {
if (!new.target) {
throw new Error("请使用new调用");
}
}
# this
从JavaScript执行上下文视角讲this: 执行上下文中包含了变量环境、词法环境、外部环境,和this, this 是和执行上下文绑定的,也就是说每个执行上下文中都有一个 this。
从使用场合视角讲this:
1.以函数的形式(包括普通函数、定时器函数、立即执行函数)调用时,this 的指向永远都是 window。比如
fun();
相当于window.fun();
2.以方法的形式调用时,this 指向调用方法的那个对象
3.以构造函数的形式调用时,this 指向实例对象
4.以事件绑定函数的形式调用时,this 指向绑定事件的对象
5.使用 call 和 apply 调用时,this 指向指定的那个对象 (call,apply 的第一个参数)
注意点:
- 避免多层 this
- 避免数组处理方法中的 this
- 避免回调函数中的 this
# 原型、原型链
JavaScript 通过构造函数生成实例对象,但是有一个缺点。同一个构造函数生成多个实例对象,无法共享属性,造成对系统资源的浪费。要解决这个问题,就需要原型对象, 原型对象上的所有属性和方法,都能被实例对象共享。JavaScript 规定,每个函数都有一个prototype属性,指向一个对象。这个对象就是原型。构造函数在生成实例的时候, 该prototype属性会自动成为实例对象的原型。
JavaScript 规定,所有对象都有自己的原型对象(prototype)。一方面,任何一个对象,都可以充当其他对象的原型;另一方面,由于原型对象也是对象,所以它也有自己的原型。因此,就会形成一个“原型链”
在 JavaScript 中,对象有一个特殊的隐藏属性 [[Prototype]],它要么为 null,要么就是对另一个对象的引用。该对象被称为“原型”。 原型链:原型就是一个对象,也有一个特殊的隐藏属性 [[Prototype]],它要么为 null,要么就是对另一个对象的引用。通过[[Prototype]] 隐藏属性连接成一条线。
var obj = {
name: "jason",
};
var obj1 = Object.create(null);
console.log(obj, "obj");
console.log(obj1, "obj1");
属性 [[Prototype]] 是内部的而且是隐藏的,设置方式:__proto__
let animal = {
eats: true,
};
let rabbit = {
jumps: true,
};
rabbit.__proto__ = animal; // (*)
// 现在这两个属性我们都能在 rabbit 中找到:
alert(rabbit.eats); // true (**)
alert(rabbit.jumps); // true
# F.prototype
如果 F.prototype 是一个对象,那么 new 操作符会使用它为新对象设置 [[Prototype]]。 这里的 F.prototype 指的是 F 的一个名为 "prototype" 的常规属性。这听起来与“原型”这个术语很类似,但这里我们实际上指的是具有该名字的常规属性。 下面是一个例子:
let animal = {
eats: true,
};
function Rabbit(name) {
this.name = name;
}
Rabbit.prototype = animal;
let rabbit = new Rabbit("White Rabbit"); // rabbit.__proto__ == animal
alert(rabbit.eats); // true
设置 Rabbit.prototype = animal 的字面意思是:“当创建了一个 new Rabbit 时,把它的 [[Prototype]] 赋值为 animal”。
- F.prototype 属性(不要把它与 [[Prototype]] 弄混了)在 new F 被调用时为新对象的 [[Prototype]] 赋值。
- F.prototype 的值要么是一个对象,要么就是 null:其他值都不起作用。
- "prototype" 属性仅在设置了一个构造函数(constructor function),并通过 new 调用时,才具有这种特殊的影响。
# JavaScript 执行机制
分为编译阶段
和执行阶段
输入一段代码,经过编译后,会生成两部分内容:执行上下文(Execution context)和可执行代码。
执行上下文:是 JavaScript 执行一段代码时的运行环境,比如调用一个函数,就会进入这个函数的执行上下文,确定该函数在执行期间用到的诸如 this、变量、对象以及函数等。 在执行上下文中存在一个变量环境的对象(Viriable Environment),该对象中保存了变量提升的内容
showName();
console.log(myname);
var myname = "jason";
function showName() {
console.log("函数showName被执行");
}
// 我们可以一行一行来分析上述代码:
// 1. 第1行和第2行,由于这两行代码不是声明操作,所以JavaScript引擎不会做任何处理;
// 2. 第3行,由于这行是经过var声明的,因此JavaScript引擎将在环境对象中创建一个名为myname的属性,并使用undefined对其初始化;
// 3. 第4行,JavaScript引擎发现了一个通过function定义的函数,所以它将函数定义存储到堆(HEAP)中,并在环境对象中创建一个showName的属性,然后将该属性值指向堆中函数的位置,这样就生成了变量环境对象。
# 作用域
- 概念:通俗来讲,作用域是一个变量或函数的作用范围。作用域在函数定义时,就已经确定了。
- 目的:为了提高程序的可靠性,同时减少命名冲突。
- 分类:
- 全局作用域:作用于整个 script 标签内部,或者作用域一个独立的 JS 文件。
- 函数作用域(局部作用域):作用于函数内的代码环境。
- 作用域的访问关系:在内部作用域中可以访问到外部作用域的变量,在外部作用域中无法访问到内部作用域的变量。
# 作用域链
作用域链:内部函数访问外部函数的变量,采用的是链式查找的方式来决定取哪个值,这种结构称之为作用域链。查找时,采用的是就近原则。
一般情况下,变量取值到创建这个变量的函数的作用域中取值。但是如果在当前作用域中没有查到值,就会向上级作用域去查,直到查到全局作用域,这么一个查找过程形成的链条就叫做作用域链。 JS 中的作用域链主要用于解析变量的值。 如果没有这个,在不同的作用域内定义了许多变量,JS 很难为变量选择某个值。
var name = "jason";
function fn() {
// 外部函数
var name = "lee";
function func() {
// 内部函数
console.log(name);
}
func();
}
fn();
# 闭包
- 概念:指有权访问另一个函数作用域中变量的函数。
- 作用:
- 延伸变量的作用范围
- 封装私有方法
- 缺点: 造成内存泄漏
# 事件简介
事件的三要素: 事件源、事件、事件驱动程序。 网页上弹出一个广告,我点击右上角的 X,广告就关闭了。这件事情里,事件源是:X。事件是:onclick。事件驱动程序是:广告关闭了。
绑定事件的两种方式/DOM 事件的级别:
- DOM0 的写法:onclick
element.onclick = function() {};
- DOM2 的写法:addEventListener
事件传播的三个阶段:事件捕获、事件冒泡和事件目标。
事件捕获阶段:事件从祖先元素往子元素查找(DOM 树结构),直到捕获到事件目标 target。在这个过程中,默认情况下,事件相应的监听函数是不会被触发的。
事件目标:当到达目标元素之后,执行目标元素该事件相应的处理函数。如果没有绑定监听函数,那就不执行。
事件冒泡阶段:事件从事件目标 target 开始,从子元素往冒泡祖先元素冒泡,直到页面的最上一级标签。
阻止冒泡: event.stopPropagation(); 事件委托:就是把一个元素响应事件(click、keydown......)的函数委托到另一个元素。
# Promise,async/await
promise 是 js 中的一个对象,用于生成可能在将来产生结果的值。 值可以是已解析的值,也可以是说明为什么未解析该值的原因。
promise 可以有三种状态:
- pending:初始状态,既不是成功也不是失败
- fulfilled:意味着操作完全成功
- rejected:意味着操作失败
一个等待状态的 promise 对象能够成功后返回一个值,也能失败后带回一个错误 当这两种情况发生的时候,处理函数会排队执行通过 then 方法会被调用
# 操作节点 API 总结
查找节点
- document.getElementById :根据 ID 查找元素,大小写敏感,如果有多个结果,只返回第一个
- document.getElementsByClassName :根据类名查找元素,多个类名用空格分隔,返回一个 HTMLCollection 。
- document.getElementsByTagName :根据标签查找元素, * 表示查询所有标签,返回一个 HTMLCollection 。
- document.getElementsByName :根据元素的 name 属性查找,返回一个 NodeList 。
- document.querySelector :返回单个 Node,如果匹配到多个结果,只返回第一个。
- document.querySelectorAll :返回一个 NodeList。
- document.forms :获取当前页面所有 form,返回一个 HTMLCollection ;
创建节点
createElement 创建元素 createTextNode 创建文本节点 cloneNode 克隆一个节点 createDocumentFragment 创建文档碎片,主要是用来存储临时节点,大量操作 DOM 时用它可以大大提升性能
- 修改节点
appendChild insertBefore removeChild replaceChild