背景
这两天复习了一下前端的一些初级的基础知识,很多东西长时间不看,就会只记得一个大概,再加深一下印象。
闭包
堆栈
深浅克隆 demo4.js
对象最好不要超过7层
浅克隆就是只克隆了第一层
堆栈内存和闭包作用域 -> demo1
函数堆对象 + 上下文作用域 = 闭包
闭包: 私有化变量
- 堆: 存储引用类型值的空间
- 栈: 存储基本类型的值和指定代码的环境
symbol
valueOf
一道阿里的函数的题录 demo5.js
、
同步异步
异步: async await setTimeout Promise
事件队列: (先执行微任务,再执行宏任务)
- 微任务: await resolve() 【哪个在前面,先执行哪个,队列】
- 宏任务: setTimeout 事件绑定 ajax
PS : new Promise 的时候,会立即执行里面的函数
栈
箭头函数
不能被new, 没有原型, 没有构造函数
柯里化
下面就是函数柯里化
const curried = (a) => {
return (b) => {
return a + b;
}
}
const addTen = curried(5);
addTen(5); // 10
手写深拷贝
function deepClone(obj = {}) {
if (typeof obj !== 'object' || obj === null) {
return obj;
}
let result;
if (obj instanceof Array) {
result = [];
} else {
result = {}
}
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
result[key] = deepClone(obj[key]);
}
}
return result;
}
基础类型
基础类型, 直接存在栈里; 引用类型是存放内存地址在栈里
引用类型的实际的值, 存在堆里面
引用类型
对象,数组,null, 特殊引用类型,函数function
typeof 运算符
- 可以识别出所有的值类型
- 判断函数
- 判断是否是引用类型
类型转化
if (obj.a == null) {}
// 相当于
if (obj.a === null || obj.a === undefined) {}
truly 变量: !!a === true
class
class的本质是函数,可见是语法糖
typeof People === 'function'
每个class都有显式原型,
每个实例都有隐式原型,指向class的显示原型
原型
隐式原型: xialuo.__proto__
等于
显式原型: Student.prototype
原型链
作用域
函数作用域
全局作用域
ES6块级作用域
自由变量
一个变量在当前作用域没有定义,但被使用了
向上级作用域,一层一层的依次寻找,直到找到为止
闭包 closure
- 函数作为参数被传递
- 函数作为返回值被返回
应该在定义时候的作用域,向上级寻找
// 函数作为返回值
function createFun () {
let a = 100;
return function () {
console.log(a) // 应该在定义时候的作用域,向上级寻找
}
}
let fn = createFun();
let a = 200;
fn() // 100
// 函数作为参数
function print(fn) {
let a = 100;
fn()
}
let a = 200;
function fn () {
console.log(a) // 应该在定义时候的作用域,向上级寻找
}
print(fn) // 200
总结: 自由变量的查找,是在函数定义的地方,向上级查找,不是在执行的地方 闭包影响: 变量会常驻内存,得不到释放 ,不能乱用
手写深拷贝
function deepClone(obj) {
if (typeof obj !== 'object' || obj == null) {
return obj;
}
let result;
if (obj instanceof Array) {
result = [];
} else {
result = {};
}
for(let key in obj) {
// 保证key 不是原型的属性
if (obj.hasOwnProperty(key)) {
result[key] = deepClone(obj[key]);
}
}
return result;
}
Object.assign() 不是深拷贝, 浅层拷贝
requestAnimateFrame
作用域和闭包
this
function fn () {
console.log(this);
}
const fn1 = fn.call({a: 1}) // {a: 1}
fn1() // {a: 1}
const fn2 = fn.bind({b: 1})
fn2(); // {b: 1}
手写bind 函数
Function.prototype.bind = function () {
// 参数转化为数组
let args = Array.prototype.slice.call(arguments);
// 数组的第一个参数,作为要绑定的this
const p = args.shift();
// 当前函数
const that = this;
return function () {
return that.apply(p, args);
}
}
手写apply
Function.prototype.apply = function (context) {
if (context == null) {
context = window;
}
context.fn = this;
const args = [...arguments].slice(1);
let result;
if (args[0]) {
result = context.fn(...args[0]);
} else {
result = context.fn();
}
delete context.fn
return result;
}
手写call
Function.prototype.call = function (context) {
let args = [...arguments].slice(1);
context.fn = this;
const result = context.fn(...args);
delete context.fn;
return result;
}
异步
// 1,3,5,4,2
console.log(1);
setTimeout(() => {
console.log(2);
}, 1000)
console.log(3);
setTimeout(() => {
console.log(4);
}, 0)
console.log(5);
DOM API
// 创建文档片段,目的是为了减少操作DOM的次数
const frag = document.createDocumentFragment();
事件
- 事件绑定
- 事件冒泡
- 事件代理
手写一个ajax
XMLHttpRequest
const xhr = new XMLHttpRequest();
// true 指的是异步的请求
xhr.open('GET', '/v1/url/id', true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.state === 200) {
}
}
}
xhr.send(null);
跨域
- jsonp
- CORS 设置 http header
cookie
- 存储大小,最大4KB
- http请求的时候需要发送到服务器端,增加了请求的数据量
- API很难用
localStorage & sessionStorage
- HTML5专门为了存储而设计的,最大可存储5M,而且是针对域名来说的
- API简单
区别
- localStorage 数据会永久存储,除非代码或手动删除
- sessionStorage 数据只存在于当前会话,浏览器关闭则清空
隐式类型转换
if , 逻辑运算, ==, +号
手写深度比较, isEqual
主要是用了递归
function isObject(params) {
return typeof params === 'object' && params !== null;
}
function isEqual(obj1, obj2) {
if (!isObject(obj1) || !isObject(obj2)) {
return obj1 === obj2;
}
if (obj1 === obj2) {
return true
}
const len1 = Object.keys(obj1);
const len2 = Object.keys(obj2);
if (len1 !== len2) return false;
for (let key in obj1) {
const res = isEqual(obj1[key], obj2[key]);
if (!res) return false;
}
return true;
}
函数声明,函数表达式
- 函数声明,会预加载
- 函数表达式
Object
- new Object() 创建一个对象,也就是 {}, 自带prototype
- Object.create() 必须传入一个参数
// 创建一个空对象,然后把原型指向传入的参数
Object.create(null) // 这样就没有原型了
Object.create({aaa: 1}) // 指定一个原型 {aaa: 1}
this 场景题
函数 this 的值,在执行的时候,才知道
const user = {
name: 'lys',
sum: () => {
console.log(this.name); // undefined
},
getName: function (params) {
console.log(this.name); // lys
}
}
const fun = user.sum;
console.log(user.sum()); // undefined
fun(); // undefined
作用域
// 返回4个4, 因为index的作用域在外层
let index;
for (index = 0; index < 4; index++) {
setTimeout(() => {
console.log(index);
}, 0);
}
手写trim
String.prototype.trim = function (params) {
return this.replace(/^\s+/, '').replace(/\s+$/, '');
};
捕获JS中的异常
window.onerror = function () {
}
什么是JSON
- JSON是一种数据格式,本质上是一段字符串
- JSON格式和JS对象结构一致,对JS语言更友好
获取当前页面URL
- location.search
- URLSearchParams, 新API 兼容性不太好 这个很简单
substr(start, length) substring(start, end) slice(start, end)
function query(name) {
const search = location.search.substr(1);
// * 是0个或多个 >=0
const reg = new RegExp(`(^|&)${name}=([^&]*)(&|$)`, 'i');
const res = search.match(reg);
if (res == null) {return null}
return res[2];
}
将URL参数解析为JS对象
手写flatern 考虑多层级
也就是拍平
// 只能拍平一层
Array.prototype.concat.apply([], arr);
function flat(arr) {
// 判断各个元素里面是否有数组
const isDeep = arr.some((item) => item instanceof Array);
// 递归的暂停条件
if (!isDeep) {
return arr;
}
const res = Array.prototype.concat.apply([], arr);
return flat(res); // 递归
}
数组去重
- forEach 循环,然后使用indexOf,或者includes来判断新数组,是否有这个item
- Set方式
function unique(arr) {
const set = new Set(arr);
return [...set];
}
instanceof
instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上
// 语法
object instanceof constructor