JS
一、数据类型
1、基本类型
Number、String、Boolean、Undefined、Null、Symbol
2、引用类型
Object、Array、Function,其他:Date、Map、Set
3、存储区别
基本类型再栈中、引用类型在堆中
二、数据结构
数组、栈、队列、链表、哈希表、字典、数、图、堆
三、DOM的常见操作
文档对象模型(DOM),元素节点、文本节点、属性节点
1、创建节点
// 元素节点
const divEl = document.createElement("div");
// 文本节点
const textEl = document.createTextNode("content");
// 文档碎片
const fragment = document.createDocumentFragment();
// 属性节点
const dataAttribute = document.createAttribute('custom');
2、获取节点
// querySelect
document.querySelector('.element')
document.querySelector('#element')
document.querySelector('div')
document.querySelector('[name="username"]')
document.querySelector('div + p > span')
// querySelectAll
const notLive = document.querySelectorAll("p");
// 其他
getElementById、getElementsByClassName、getElementsByTagName、getElementsByName
3、更新节点
// <p id="p">...</p >
var p = document.getElementById('p');
// abc:
p.innerHTML = 'ABC'; // <p id="p">ABC</p >
// HTML:
p.innerHTML = 'ABC <span style="color:red">RED</span> XYZ';
// <p id="p-id">...</p >
var p = document.getElementById('p-id');
// :
p.innerText = '<script>alert("Hi")</script>';
// HTML <script> :
// <p id="p-id"><script>alert("Hi")</script></p >
// <p id="p-id">...</p >
const p = document.getElementById('p-id');
// CSS:
p.style.color = '#ff0000';
p.style.fontSize = '20px'; //
p.style.paddingTop = '2em';
4、添加节点
// innerHtml
// appendChild
<!-- HTML -->
<p id="js">JavaScript</p >
<div id="list">
<p id="java">Java</p >
<p id="python">Python</p >
<p id="scheme">Scheme</p >
</div>
const js = document.getElementById('js')
js.innerHTML = "JavaScript"
const list = document.getElementById('list');
list.appendChild(js);
<!-- HTML -->
<div id="list">
<p id="java">Java</p >
<p id="python">Python</p >
<p id="scheme">Scheme</p >
<p id="js">JavaScript</p >
</div>
// insertBefore
parentElement.insertBefore(newElement, referenceElement)
// setAttribute
const div = document.getElementById('id')
div.setAttribute('class', 'white');
5、删除节点
// removeChild
const self = document.getElementById('to-be-removed');
// :
const parent = self.parentElement;
// :
const removed = parent.removeChild(self);
removed === self; // true
四、BOM对象
BOM --> Browser Object Model
1、BOM的常见对象
window、location、navigator、screen、history
2、window(核心对象)
3、location
http://foouser:barpassword@www.wrox.com:80/WileyCDA/?q=javascript#contents
hash、host、hostname、href、pathname、port、protocol、search
3、navigator
用于获取浏览器的属性、配置
4、screen
5、history
操作浏览器URL的历史记录
history.go()
、history.forward()
、history.back()
、history.length
五、==和===的区别
1、==(相等操作符)
将布尔转换为数值
一个式字符串、一个是数值,将字符串转换为数值
一个是对象、一个不是对象、调用对象的valueOf或者原始值
null和undefined相等
NaN返回false
2、全等操作符
只有再不转换的前提下相等才会返回true
3、区别
==会做类型转换然后进行值得比较,全等不会做类型转换
全等下null === undefined为false
六、typeof和instanceof区别
// typeof
typeof 1 // 'number'
// instanceof
object instanceof constructor
function myInstanceof(left, right) {
// typeof false
if(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;//
proto = Object.getPrototypeof(proto);
}
}
检测数据类型:Object.prototype.toString.call()
七、JavaScript原型、原型链
八、作用域链
全局作用域、函数作用域、块级作用域、词法作用域
九、this对象
绑定规则:默认绑定、隐式绑定、new绑定、显式修改
箭头函数:window 因为JavaScript没有块作用域,所以在定义sayThis的时候,里面的this就绑定到window上去了
十、new操作符
是什么:用于创建一个给定构造函数的实例对象,可以访问到构造函数中的属性,以及构造函数原型链中的属性。
new时的工作:创建一个新的对;将对象与构建函数通过原型链连接起来;将构建函数中的this绑定到新建的对象obj上;根据构建函数返回类型做判断,如果是原始值则被忽略,如果是返回对象,需要正常处理。
模拟实现new:
function myNew(fun, ...args) {
const obj = {} // 创建一个空对象
obj.__proto__ = fun.prototype // 将新对象原型指向构造函数原型
const result = fun.apply(obj, ...agrs) // 将构造函数的this指向新建的obj对象上
return result instanceof Object ? result : obj // 根据返回值做判断
}
十一、bind、call、apply区别
共同作用:改变函数执行时的上下文,改变函数运行时的this指向
1、apply
// 第一个参数是this的指向,第二个参数是函数接受的参数,数组形式传入
function fn(...args){
console.log(this,args);
}
let obj = {
myname:" "
}
fn.apply(obj,[1,2]); // this会变成传入的obj,传入的参数需要是一个数组
fn(1,2) // this指向window
实现:
Function.prototype.myApply = function (context, args) {
// 1. 判断args的类型,如果不是Array的实例,抛出一个TypeError;
if(!(args instanceof Array)){
throw new TypeError(`args is not an array!`)
}
// 2. 确定要绑定的对象,即最终谁来调用函数,命名为new_this;若没有传入要绑定的对象, 默认绑定window对象
const new_this = context || window
// 3. 把方法作为对象的属性绑定给new_this,但要注意,也许原有属性就有func,为了避免冲突,这里用symbol
const func = Symbol('func')
//由于这里func是Symbol变量不再是字符串,所以不能再用new_this.func而是要用中括号获取属性
//下面的this为调用我们正在写的myApply函数的函数,比如:fn1.myApply(context, args);此时this为fn1
new_this[func] = this
// 4. 执行当前函数,并获取返回值
const res = new_this[func](...args)
// 5. 删除我们绑定的的Symbol(func)属性,以免污染new_this的属性
delete new_this[func]
// 6. 返回第3步得到的返回值
return res
}
2、call
// 第一个参数是this的指向,后面传入的是一个参数列表
function fn(...args){
console.log(this,args);
}
let obj = {
myname:" "
}
fn.apply(obj,1,2); // this会变成传入的obj,传入的参数需要是一个分开的
fn(1,2) // this指向window
实现:
Function.prototype.myCall = function (context, ...args) {
// if(typeof this !== 'function'){ //不需要判断类型,因为myCall定义在Function.prototype上
// throw new TypeError(`${this} is not a function!`)
// }
// 1. 确定要绑定的对象,即最终谁来调用函数,命名为new_this;若没有传入要绑定的对象, 默认绑定window对象
const new_this = context || window
// 2. 把方法作为对象的属性绑定给new_this,但要注意,也许原有属性就有func,为了避免冲突,这里用symbol
const func = Symbol('func')
//由于这里func是Symbol变量不再是字符串,所以不能再用new_this.func而是要用中括号获取属性
new_this[func] = this
// 3. 执行当前函数,并获取返回值
const res = new_this[func](...args)
// 4. 删除我们绑定的的Symbol(func)属性,以免污染new_this的属性
delete new_this[func]
// 5. 返回第3步得到的返回值
return res
}
3、bind
// 第一个参数是this的指向,后买你传入的是参数列表
function fn(...args){
console.log(this,args);
}
let obj = {
myname:" "
}
const bindFn = fn.bind(obj); // this会变成传入的obj,传入的参数需要是一个分开的,不会立即执行,而是返回一个永久改变this指向的函数
bindFn(1,2) // this指向obj,传入的参数需要是一个分开的
fn(1, 2) // this指向window
实现:
// bind
fn.bind(obj,1,2)()
// bind
fn.bind(obj,1)(2)
Function.prototype.myBind = function (context, ...args) {
// 1. 确定要绑定的对象,即最终谁来调用函数,命名为new_this;若没有传入要绑定的对象, 默认绑定window对象
context = context || window
// 2. 把原函数(即this)用一个fn变量保存一下,这样更能看出它表示一个函数
let fn = this
return function newFn (...fnArgs) {
let res
// 3.要考虑新函数是不是会当作构造函数
if (this instanceof newFn) {
// 如果是构造函数则调用new 并且合并参数args,fnArgs
res = new fn(...args, ...fnArgs)
} else {
// 当作普通函数调用 也可以用上面定义的myCall
res = fn.call(context, ...args, ...fnArgs)
}
return res
}
}
十二、上下文和执行栈是什么
全局执行上下文、函数执行上下文、Eval执行上下文
1、执行上下文的生命周期
创建阶段:确定this的值;LexicalEnvironment(词法环境)组件被创建;VariableEnvironment(变量环境)组件被创建
执行阶段:执行变量赋值、代码执行
回收阶段:执行上下文出栈等待虚拟机回收执行上下文
2、执行栈
调用栈,后进先出结构
十三、事件模型
事件与事件流、原始事件模型、标准事件模型、IE事件模型
1、事件与事件流
事件流的阶段:事件捕获阶段、处于目标阶段、事件冒泡阶段
2、原始事件模型
// 方式一
<input type="button" onclick="fun()">
// 方式二
var btn = document.getElementById('.btn');
btn.onclick = fun;
// 删除原始事件模型,将对应事件属性设置为null
btn.onclick = null;
3、标准事件模型
// 事件绑定监听函数
addEventListener(eventType, handler, useCapture)
// 事件移除监听函数
removeEventListener(eventType, handler, useCapture)
eventType:事件类型,不加on
handler:事件处理函数
useCapture:指定是否在捕获阶段处理,默认为false,
false:child-->parent-->grantParent
true:grantParent-->parent-->child
4、IE事件模型
过程:
事件处理阶段:事件到达目标元素,触发目标元素的监听函数
事件冒泡阶段:事件从目标元素冒泡到document,依次检查经过的节点是否绑定了事件监听函数,如果有则执行
// 事件绑定监听函数的方式
attachEvent(eventType, handler)
// 事件移除监听喊出的方式
deleteEvent(eventType, handler)
十四、事件代理以及应用场景
将一个元素的响应事件(click,keydown)的函数委托到另一个元素。在冒泡阶段完成,真正绑定事件的是外层元素而非目标元素。当事件相应到目标元素的时候,会通过冒泡机制从而触发他的外层元素的绑定事件上,然后再外层元素上执行函数
优点:减少整个页面所需内存,提升整体性能;动态绑定,减少重复工作。
十五、闭包
1、闭包是什么
一个函数和对齐周围状态(词法环境)的引用捆绑在一起,或者说函数被引用包围,这样的组合就是闭包。
function init() {
var name = "Mozilla"; // name 是一个被init创建的局部变量
function displayName() { // displayName() 是内部函数,一个闭包
alert(name); // 使用父函数中声明的变量
}
displayName();
}
init();
作用:防抖、节流、私有变量(迭代器)。。。
十六、类型转化机制
1、显示转换
Number()、parseInt()、String()、Boolean()
2、隐式转换
十七、深拷贝和浅拷贝的区别
1、浅拷贝
拷贝的基本类型的值,或者引用类型的内存地址,只拷贝一层。
function shallowClone(obj) {
const newObj = {};
for(let prop in obj) {
if(obj.hasOwnProperty(prop)){
newObj[prop] = obj[prop];
}
}
return newObj;
}
Object.assgin、Array.prototype.slice()、Array.prototype.concat()
使用扩展运算符
2、深拷贝
会开辟一个新的栈,拷贝的是所有的值。
_.cloneDeep()、jQuery.extend()、JSON.parse(JSON.stringfy())、手写递归循环
JSON.parse(JSON.stringfy())会忽略undefined、Symbol、函数
十八、如何实现函数缓存,以及场景
函数缓存就是将函数运算的结果进行缓存,用空间(缓存存储)换时间(计算过程)
实现:闭包、柯里化、高阶函数
const memoize = function (func, content) {
let cache = Object.create(null)
content = content || this
return (...key) => {
if (!cache[key]) {
cache[key] = func.apply(content, key)
}
return cache[key]
}
}
const calc = memoize(add);
const num1 = calc(100,200)
const num2 = calc(100,200) // 得到缓存的结果
十九、JavaScript字符串常用方法
1、操作方法(增删改查)
// 增:+、${}、concat
"hello" + " world"
"hello".concat(" world")
// 删
"hello world".slice(3) // lo world
"hello world".substring(3) // lo world
"hello world".substr(3) // lo world
"hello world".slice(3, 7) // lo w
"hello world".substring(3, 7) // lo w
"hello world".substr(3, 7) // lo worl
// 改
" hello world ".trim() // hello world
"na g".repeat(2) // na gna g
"foo".padStart(6) // ' foo'
"foo".padStart(9, ".") // '......foo'
"hello world".toUpperCase() // HELLO WORLD
"hello world".toLowerCase() // hello world
// 查
"abcde".charAt(2) // c
"hello world".indexOf("o") // 4
"foobarbaz".startsWith("foo") // true
"foobarbaz".startsWith("bar") // false
"foobarbaz".includes("bar") // true
"foobarbaz".includes("qux") // false
2、转换方法
"12+23+34".split("+") // [12, 23, 34]
4、模板匹配方法
"cat, bat, sat, fat".match(/.at/) // "cat"
"cat, bat, sat, fat".search(/at/) // 1(找到返回索引,否则返回-1)
"cat, bat, sat, fat".replace("at", "ond") // "cond, bat, sat, fat"
二十、数组常用方法
1、操作方法
// 增
const colors = []
colors.push("red", "green") // ["red", "green"]
colors.push("yellow") // ["red", "green", yellow]
colors.unshift("blue", "pink") // ["blue", "pink", "red", "green", yellow]
colors.splice(1, 0, "white", "black") // ["blue", "white", "black", "pink", "red", "green", yellow]
["red", "green"].concat("yellow", ["pink"]) // ["red", "green", "yellow", "pink"] 返回新数组
// 删
colors.pop() // ["blue", "white", "black", "pink", "red", "green"] 删除最后一项返回删除的项
colors.shift() // ["white", "black", "pink", "red", "green"] 删除第一项返回删除的项
colors.splice(0, 1) // ["black", "pink", "red", "green"] 返回删除元素的数组
colors.slice(1) // ["pink", "red", "green"]
// 改
colors.splice(1, 1, "blue", "black") // ["pink", "blue", "black", "green"]
// 查
[1,2,3,4,5].idnexOf(4) // 3
[1,2,3,4,5].includes(4) // true
[1,2,3,4,5].find((item, index, array) => item === 4) // 2
2、排序方法
[1,2,3,4,5].reverse() // [5,4,3,2,1]
function compare(value1, value2) {
return value1 - value2
}
let values = [0, 1, 5, 10, 15];
values.sort(compare);
alert(values); // 0,1,5,10,15
3、转换方法
["red", "green", "blue"].join(",") // red,green,blue
4、迭代方法
// some 至少有一个满足条件,则返回true
[1,2,3,4].some((itme, index, array) => item > 2) // true
// every 所有的元素都满足体条件,则返回true
[1,2,3,4].every((item, index, array) => item > 2) // false
// forEach
[1,2,3,4].forEach((item, index, array) => {
// 执行某些操作
})
// filter,返回新数组
[1,2,3,4].filter((item, idnex, array) => item > 2) // [3,4]
// map,对每一项进行操作,返回新数组
[1,2,3,4].map((item, index, array) => item * 2) // [2,4,6,8]
二十一、事件循环
实现单线程非阻塞的方法是事件循环
1、宏任务与微任务
console.log(1)
setTimeout(()=>{
console.log(2)
}, 0)
new Promise((resolve, reject)=>{
console.log('new Promise')
resolve()
}).then(()=>{
console.log('then')
})
console.log(3)
// 1 --> new Promise --> 3 --> then --> 2
setTimeout(()=> console.log('a'))
console.log(1)
Promise.resolve()
.then(()=> console.log('b') )
.then(()=>
Promise.resolve('c').then((data)=> {
setTimeout(() => console.log('d'));
console.log('f');
return data;
}
)
).then(data=> console.log(data))
console.log(2)
// 1 2 b f c a d
async1()
setTimeout(() => {
new Promise((resolve, reject) => {
console.log(2);
resolve()
}).then(() => {
console.log(3);
})
}, 10)
new Promise((resolve, reject) => {
console.log(4);
resolve()
}).then(() => {
console.log(5);
})
async function async1() {
console.log(1);
await async2()
console.log(7)
}
async function async2() {
console.log(6);
}
setTimeout(() => {
new Promise((resolve, reject) => {
console.log(8);
resolve()
}).then(() => {
console.log(9);
})
})
console.log(10)
// 1 6 4 10 75 8 9 2 3
async function async1() {
console.log('async1 start')
await async2()
console.log('async1 end')
}
async function async2() {
console.log('async2')
}
console.log('script start')
setTimeout(function () {
console.log('settimeout')
})
async1()
new Promise(function (resolve) {
console.log('promise1')
resolve()
}).then(function () {
console.log('promise2')
})
console.log('script end')
// script start --> async1 start --> async2 --> promise1 --> script end --> async1 end --> promise2 --> settimeout
常见微任务:Promise.then、MutationObserve、Object.observe、process.nextTick
常见宏任务:外层同步代码、setTimeout、setInterval、UI rendering、UI 事件、postMessage、MessageChannel、setImmediate、I/O
二十二、JavaScript本地存储方式
cookie、sessionStorage、localStorage、indexedDB
1、cookie
Cookie 是由服务器生成并发送到客户端的一小段数据,客户端会将其存储并在后续请求中携带,帮助服务器识别用户。Cookie 主要用于以下场景:
- 用户身份认证(如记住登录状态)
- 存储用户偏好设置
- 跟踪用户行为(如广告点击记录)
// 设置 Cookie
document.cookie = "username=John Doe; expires=Fri, 31 Dec 2024 12:00:00 UTC; path=/";
// 读取 Cookie
console.log(document.cookie);
2、session
Session 是存储在服务器端的用户会话信息。每个用户会话都有一个唯一的 Session ID,服务器通过这个 ID 来识别不同的用户。客户端通过 Cookie 将 Session ID 发送给服务器,从而实现用户识别
// 使用 Express 和 express-session 中间件
const express = require('express');
const session = require('express-session');
const app = express();
app.use(session({
secret: 'my secret',
resave: false,
saveUninitialized: true,
cookie: { secure: false }
}));
app.get('/', (req, res) => {
if (req.session.views) {
req.session.views++;
res.send(`Number of views: ${req.session.views}`);
} else {
req.session.views = 1;
res.send('Welcome to the session demo. Refresh!');
}
});
app.listen(3000);
3、localStorage
4、sessionStorage
5、IndexedDB
二十三、防抖和节流
二十四、上拉加载和下拉刷新
1、上拉加载
scrollTop + clientHeight >= scrollHeight