Skip to content

JS

一、数据类型

1、基本类型

Number、String、Boolean、Undefined、Null、Symbol

2、引用类型

Object、Array、Function,其他:Date、Map、Set

3、存储区别

基本类型再栈中、引用类型在堆中

二、数据结构

数组、栈、队列、链表、哈希表、字典、数、图、堆

三、DOM的常见操作

文档对象模型(DOM),元素节点、文本节点、属性节点

1、创建节点

js
// 元素节点 
const divEl = document.createElement("div");
// 文本节点
const textEl = document.createTextNode("content");
// 文档碎片
const fragment = document.createDocumentFragment();
// 属性节点
const dataAttribute = document.createAttribute('custom');

2、获取节点

js
// 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、更新节点

js
 // <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">&lt;script&gt;alert("Hi")&lt;/script&gt;</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、添加节点

js
// 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、删除节点

js
// 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

js
 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区别

js
// 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:

js
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

js
// 第一个参数是this的指向,第二个参数是函数接受的参数,数组形式传入
function fn(...args){
	 console.log(this,args);
 }
 let obj = {
	 myname:" "
 }
 fn.apply(obj,[1,2]); // this会变成传入的obj,传入的参数需要是一个数组
 fn(1,2) // this指向window

实现:

js
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

js
// 第一个参数是this的指向,后面传入的是一个参数列表
function fn(...args){
	 console.log(this,args);
 }
 let obj = {
	 myname:" "
 }
 fn.apply(obj,1,2); // this会变成传入的obj,传入的参数需要是一个分开的
 fn(1,2) // this指向window

实现:

js
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

js
// 第一个参数是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

实现:

js
 // bind
 fn.bind(obj,1,2)()
 // bind
 fn.bind(obj,1)(2)
js
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、原始事件模型

js
// 方式一
 <input type="button" onclick="fun()">

// 方式二
var btn = document.getElementById('.btn');
 btn.onclick = fun;

// 删除原始事件模型,将对应事件属性设置为null
 btn.onclick = null;

3、标准事件模型

js
// 事件绑定监听函数
addEventListener(eventType, handler, useCapture)
// 事件移除监听函数
 removeEventListener(eventType, handler, useCapture)

eventType:事件类型,不加on
handler:事件处理函数
useCapture:指定是否在捕获阶段处理,默认为false,
false:child-->parent-->grantParent
true:grantParent-->parent-->child

4、IE事件模型

过程:

事件处理阶段:事件到达目标元素,触发目标元素的监听函数

事件冒泡阶段:事件从目标元素冒泡到document,依次检查经过的节点是否绑定了事件监听函数,如果有则执行

js
// 事件绑定监听函数的方式
attachEvent(eventType, handler)
// 事件移除监听喊出的方式
deleteEvent(eventType, handler)

十四、事件代理以及应用场景

将一个元素的响应事件(click,keydown)的函数委托到另一个元素。在冒泡阶段完成,真正绑定事件的是外层元素而非目标元素。当事件相应到目标元素的时候,会通过冒泡机制从而触发他的外层元素的绑定事件上,然后再外层元素上执行函数

优点:减少整个页面所需内存,提升整体性能;动态绑定,减少重复工作。

十五、闭包

1、闭包是什么

一个函数和对齐周围状态(词法环境)的引用捆绑在一起,或者说函数被引用包围,这样的组合就是闭包。

js
function init() {
     var name = "Mozilla"; // name 是一个被init创建的局部变量
    function displayName() { // displayName() 是内部函数,一个闭包
    	alert(name); // 使用父函数中声明的变量
    }
     displayName();
 }
 init();

作用:防抖、节流、私有变量(迭代器)。。。

十六、类型转化机制

1、显示转换

Number()、parseInt()、String()、Boolean()

2、隐式转换

十七、深拷贝和浅拷贝的区别

1、浅拷贝

拷贝的基本类型的值,或者引用类型的内存地址,只拷贝一层。

js
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、函数

十八、如何实现函数缓存,以及场景

函数缓存就是将函数运算的结果进行缓存,用空间(缓存存储)换时间(计算过程)

实现:闭包、柯里化、高阶函数

js
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、操作方法(增删改查)

js
// 增:+、${}、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、转换方法

js
"12+23+34".split("+") // [12, 23, 34]

4、模板匹配方法

js
 "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、操作方法

js
// 增
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、排序方法

js
[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、转换方法

js
["red", "green", "blue"].join(",") // red,green,blue

4、迭代方法

js
// 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、宏任务与微任务

js
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
js
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
js
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
js
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 主要用于以下场景:

  • 用户身份认证(如记住登录状态)
  • 存储用户偏好设置
  • 跟踪用户行为(如广告点击记录)
js
// 设置 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 发送给服务器,从而实现用户识别

js
// 使用 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、上拉加载

js
scrollTop + clientHeight >= scrollHeight

2、下拉刷新

二十四、正则表达式

二十五、递归