赞
踩
答:
数据类型分为基本数据类型和引用数据类型;
基本数据类型有:
引用数据类型统称为Object类型,细分的话有:
基本数据类型的数据直接存储在栈中;而引用数据类型的数据存储在堆中,在栈中保存数据的引用地址,这个引用地址指向的是对应的数据,以便快速查找到堆内存中的对象。
顺便提一句,栈内存是自动分配内存的。而堆内存是动态分配内存的,不会自动释放。所以每次使用完对象的时候都要把它设置为null,从而减少无用内存的消耗
答:
因为在JS底层中,每个变量是以二进制表示,固定长度为64位,其中第1位是符号位,再往后11位是指数为,最后52表示的是尾数位,而0.1和0.2转为二进制的时候是无限循环小数,所以JS就会进行截取,截取以后0.1和0.2就不是他们本身了,要比原来大那么一丢丢,所以0.1+0.2就>0.3了
答:
先给他们放大倍数,随后在除以相应倍数
const a = 0.1;
const b = 0.2;
console.log(a + b === 0.3) // false
console.log((a * 1000 + b * 1000) / 1000 === 0.3) // true
答:
1.typeof
typeof null
的值为Object
,无法分辨是null
还是Object
2.instanceof
3.constructor
null
和 undefined
,因为这两个特殊类型没有其对应的包装对象。constructor
和instanceof
类似,constructor
返回结果的是自己的构造函数,而 instanceof
则是自己与构造函数比较返回布尔值4.Object.prototype.toString.call()
一种最好的基本类型检测方式 Object.prototype.toString.call()
;它可以区分 null 、 string 、boolean 、 number 、 undefined 、 array 、 function 、 object 、 date 、 math 数据类型。
缺点:不能细分为谁谁的实例
// -----------------------------------------typeof typeof undefined // 'undefined' typeof '10' // 'String' typeof 10 // 'Number' typeof false // 'Boolean' typeof Symbol() // 'Symbol' typeof Function // ‘function' typeof null // ‘Object’ typeof [] // 'Object' typeof {} // 'Object' // -----------------------------------------instanceof function Foo() { } var f1 = new Foo(); var d = new Number(1) console.log(f1 instanceof Foo);// true console.log(d instanceof Number); //true console.log(123 instanceof Number); //false -->不能判断字面量的基本数据类型 // -----------------------------------------constructor var d = new Number(1) var e = 1 function fn() { console.log("ming"); } var date = new Date(); var arr = [1, 2, 3]; var reg = /[hbc]at/gi; console.log(e.constructor);//ƒ Number() { [native code] } console.log(e.constructor.name);//Number console.log(fn.constructor.name) // Function console.log(date.constructor.name)// Date console.log(arr.constructor.name) // Array console.log(reg.constructor.name) // RegExp (5).constructor === Number // true "text".constructor === String // true true.constructor === Boolean // true ({}).constructor === Object // true // Uncaught TypeError: Cannot read property 'constructor' of undefined undefined.constructor === undefined // 报错 null.constructor === null // 报错 //-----------------------------------------Object.prototype.toString.call() console.log(Object.prototype.toString.call(undefined)); // "[object Undefined]" console.log(Object.prototype.toString.call(null)); // "[object Null]" console.log(Object.prototype.toString.call(123)); // "[object Number]" console.log(Object.prototype.toString.call("abc")); // "[object String]" console.log(Object.prototype.toString.call(true)); // "[object Boolean]" function fn() { console.log("ming"); } var date = new Date(); var arr = [1, 2, 3]; var reg = /[hbc]at/gi; console.log(Object.prototype.toString.call(fn));// "[object Function]" console.log(Object.prototype.toString.call(date));// "[object Date]" console.log(Object.prototype.toString.call(arr)); // "[object Array]" console.log(Object.prototype.toString.call(reg));// "[object RegExp]"
Object.prototype.toString.call()
,为什么不用 Array.prototype.toString.call()
?⭐⭐答:
因为只有Object.prototype.toString.call()返回的是统一格式,而且 Array.prototype.toString.call()的部分类型无法检验。
function fn() { console.log("ming"); } var date = new Date(); var arr = [1, 2, 3]; var reg = /[hbc]at/gi; console.log(Array.prototype.toString.call(undefined)); // 报错 console.log(Array.prototype.toString.call(null)); // 报错 console.log(Array.prototype.toString.call(123)); // "[object Number]" console.log(Array.prototype.toString.call("abc")); // "[object String]" console.log(Array.prototype.toString.call(true)); // "[object Boolean]" console.log(Array.prototype.toString.call(fn)); // "[object Function]" console.log(Array.prototype.toString.call(date)); // "[object Date]" console.log(Array.prototype.toString.call(arr)); // "1,2,3" console.log(Array.prototype.toString.call(reg));// "[object RegExp]"
function myInstance(L, R) {//L代表instanceof左边,R代表右边
var RP = R.prototype
var LP = L.__proto__
while (true) {
if(LP == null) {
return false
}
if(LP == RP) {
return true
}
LP = LP.__proto__
}
}
console.log(myInstance({},Object));
答:
因为在JavaScript中,不同的对象都是使用二进制存储的,如果二进制前三位都是0的话,系统会判断为是Object类型,而null的二进制全是0,自然也就判断为Object
这个bug是初版本的JavaScript中留下的,扩展一下其他五种标识位:
==
和===
有什么区别 ⭐⭐⭐⭐⭐答:
===
是严格意义上的相等,会比较两边的数据类型和值大小
数据类型不同返回false
数据类型相同,但值大小不同,返回false
==
是非严格意义上的相等,
两边类型相同,比较大小
两边类型不同,根据下方表格,再进一步进行比较。
NaN === NaN
返回什么?⭐⭐⭐⭐⭐返回 false
,NaN
永远不等于NaN
,判断是否为NaN
用一个函数 isNaN
来判断;
isNaN
传入的如果是其他数据类型,那么先将它使用Number()
转为数字类型再进行判断
答:
call
:
Function.prototype.myCall = function (context) { // 先判断调用myCall是不是一个函数 // 这里的this就是调用myCall的 if (typeof this !== 'function') { throw new TypeError("Not a Function") } // 不传参数默认为window context = context || window // 保存this context.fn = this // 保存参数 let args = Array.from(arguments).slice(1) //Array.from 把伪数组对象转为数组 // 调用函数 let result = context.fn(...args) delete context.fn return result }
apply
Function.prototype.myApply = function (context) { // 判断this是不是函数 if (typeof this !== "function") { throw new TypeError("Not a Function") } let result // 默认是window context = context || window // 保存this context.fn = this // 是否传参 if (arguments[1]) { result = context.fn(...arguments[1]) } else { result = context.fn() } delete context.fn return result }
bind
Function.prototype.myBind = function(context){ // 判断是否是一个函数 if(typeof this !== "function") { throw new TypeError("Not a Function") } // 保存调用bind的函数 const _this = this // 保存参数 const args = Array.prototype.slice.call(arguments,1) // 返回一个函数 return function F () { // 判断是不是new出来的 if(this instanceof F) { // 如果是new出来的 // 返回一个空对象,且使创建出来的实例的__proto__指向_this的prototype,且完成函数柯里化 return new _this(...args,...arguments) }else{ // 如果不是new出来的改变this指向,且完成函数柯里化 return _this.apply(context,args.concat(...arguments)) } } }
答:
字面量:
new内部:
手写new
// 手写一个new
function myNew(fn, ...args) {
// 创建一个空对象
let obj = {}
// 使空对象的隐式原型指向原函数的显式原型
obj.__proto__ = fn.prototype
// this指向obj
let result = fn.apply(obj, args)
// 返回
return result instanceof Object ? result : obj
}
undefined 代表的含义是未定义,null 代表的含义是空对象。一般变量声明了但还没有定义的时候会返回 undefined,null主要用于赋值给一些可能会返回对象的变量,作为初始化。
null
表示"没有对象",即该处不应该有值。典型用法是:
(1)作为函数的参数,表示该函数的参数不是对象。
(2)作为对象原型链的终点。
undefined
表示"缺少值",就是此处应该有一个值,但是还没有定义。典型用法是:
(1)变量被声明了,但没有赋值时,就等于undefined。
(2)调用函数时,应该提供的参数没有提供,该参数等于undefined。
(3)对象没有赋值的属性,该属性的值为undefined。
(4)函数没有返回值时,默认返回undefined。
ECMAScript 认为 undefined 是从 null 派生出来的,他们的值是一样的, 但是类型却不一样。
所以
null == undefined //true
null === undefined //false
答:
答:
执行上下文分为:
执行栈:
答:
函数执行,形成私有的执行上下文,使内部私有变量不受外界干扰,起到保护和保存的作用
作用:
应用:
缺点
答:
原型: 原型分为隐式原型和显式原型,每个对象都有一个隐式原型,它指向自己的构造函数的显式原型。每个构造方法都有一个显式原型。
__proto__
是隐式原型;prototype
是显式原型
所有实例的__proto__
都指向他们构造函数的prototype
所有的prototype
都是对象,自然它的__proto__
指向的是Object()
的prototype
所有的构造函数的隐式原型指向的都是Function()
的显示原型
Object的隐式原型是null
原型链: 多个__proto__
组成的集合成为原型链(概念类似于作用域链)
instanceof
就是判断某对象是否位于某构造方法的原型链上。
答:
原型继承、组合继承、寄生组合继承、ES6的extend
原型继承
// ----------------------方法一:原型继承
// 原型继承
// 把父类的实例作为子类的原型
// 缺点:子类的实例共享了父类构造函数的引用属性 不能传参
var person = {
friends: ["a", "b", "c", "d"]
}
var p1 = Object.create(person)
p1.friends.push("aaa")//缺点:子类的实例共享了父类构造函数的引用属性
console.log(p1);
console.log(person);//缺点:子类的实例共享了父类构造函数的引用属性
组合继承
// ----------------------方法二:组合继承 // 在子函数中运行父函数,但是要利用call把this改变一下, // 再在子函数的prototype里面new Father() ,使Father的原型中的方法也得到继承,最后改变Son的原型中的constructor // 缺点:调用了两次父类的构造函数,造成了不必要的消耗,父类方法可以复用 // 优点可传参,不共享父类引用属性 function Father(name) { this.name = name this.hobby = ["篮球", "足球", "乒乓球"] } Father.prototype.getName = function () { console.log(this.name); } function Son(name, age) { Father.call(this, name) this.age = age } Son.prototype = new Father() Son.prototype.constructor = Son var s = new Son("ming", 20) console.log(s);
寄生组合继承
// ----------------------方法三:寄生组合继承 function Father(name) { this.name = name this.hobby = ["篮球", "足球", "乒乓球"] } Father.prototype.getName = function () { console.log(this.name); } function Son(name, age) { Father.call(this, name) this.age = age } Son.prototype = Object.create(Father.prototype) Son.prototype.constructor = Son var s2 = new Son("ming", 18) console.log(s2);
extend
// ----------------------方法四:ES6的extend(寄生组合继承的语法糖)
// 子类只要继承父类,可以不写 constructor ,一旦写了,则在 constructor 中的第一句话
// 必须是 super 。
class Son3 extends Father { // Son.prototype.__proto__ = Father.prototype
constructor(y) {
super(200) // super(200) => Father.call(this,200)
this.y = y
}
}
答:
内存泄露是指不再用的内存没有被及时释放出来,导致该段内存无法被使用就是内存泄漏
答:
内存泄漏指我们无法在通过js访问某个对象,而垃圾回收机制却认为该对象还在被引用,因此垃圾回收机制不会释放该对象,导致该块内存永远无法释放,积少成多,系统会越来越卡以至于崩溃
答:
数组的浅拷贝: 如果是数组,我们可以利用数组的一些方法,比如 slice,concat 方法返回一个新数组的特性来实现拷贝,但假如数组嵌套了对象或者数组的话,使用 concat 方法克隆并不完整,如果数组元素是基本类型,就会拷贝一份,互不影响,
而如果是对象或数组,就会只拷贝对象和数组的引用,这样我们无论在新旧数组进行了修改,两者都会发生变化,我们把这种复制引用的拷贝方法称为浅拷贝。
深拷贝就是指完全的拷贝一个对象,即使嵌套了对象,两者也互相分离,修改一个对象的属性,不会影响另一个。
浅拷贝: 以赋值的形式拷贝引用对象,仍指向同一个地址,修改时原对象也会受到影响
Object.assign()
...
(展开运算符) — 可以将一个数组中的元素展开到另一个数组中或者作为函数的参数传递Array.concat()
Array.slice()
深拷贝: 完全拷贝一个新对象,修改时原对象不再受到任何影响
JSON.parse(JSON.stringify(obj))
: 性能最快递归
进行逐一赋值structuredClone(value)
// ----------------------------------------------浅拷贝 // 只是把对象的属性和属性值拷贝到另一个对象中 var obj1 = { a: { a1: { a2: 1 }, a10: { a11: 123, a111: { a1111: 123123 } } }, b: 123, c: "123" } // 方式1 function shallowClone1(o) { let obj = {} for (let i in o) { obj[i] = o[i] } return obj } // 方式2 var shallowObj2 = { ...obj1 } // 方式3 var shallowObj3 = Object.assign({}, obj1) let shallowObj = shallowClone1(obj1); shallowObj.a.a1 = 999 shallowObj.b = true console.log(obj1); //第一层的没有被改变,一层以下就被改变了 // ----------------------------------------------深拷贝 // 简易版 function deepClone(o) { let obj = {} for (var i in o) { // if(o.hasOwnProperty(i)){ if (typeof o[i] === "object") { obj[i] = deepClone(o[i]) } else { obj[i] = o[i] } // } } return obj } var myObj = { a: { a1: { a2: 1 }, a10: { a11: 123, a111: { a1111: 123123 } } }, b: 123, c: "123" } var deepObj1 = deepClone(myObj) deepObj1.a.a1 = 999 deepObj1.b = false console.log(myObj); // 简易版存在的问题:参数没有做检验,传入的可能是 Array、null、regExp、Date function deepClone2(o) { if (Object.prototype.toString.call(o) === "[object Object]") { //检测是否为对象 let obj = {} for (var i in o) { if (o.hasOwnProperty(i)) { if (typeof o[i] === "object") { obj[i] = deepClone(o[i]) } else { obj[i] = o[i] } } } return obj } else { return o } } function isObject(o) { return Object.prototype.toString.call(o) === "[object Object]" || Object.prototype.toString.call(o) === "[object Array]" } // 继续升级,没有考虑到数组,以及ES6中的map、set、weakset、weakmap function deepClone3(o) { if (isObject(o)) {//检测是否为对象或者数组 let obj = Array.isArray(o) ? [] : {} for (let i in o) { if (isObject(o[i])) { obj[i] = deepClone(o[i]) } else { obj[i] = o[i] } } return obj } else { return o } } // 有可能碰到循环引用问题 var a = {}; a.a = a; clone(a);//会造成一个死循环 // 循环检测 // 继续升级 function deepClone4(o, hash = new map()) { if (!isObject(o)) return o//检测是否为对象或者数组 if (hash.has(o)) return hash.get(o) let obj = Array.isArray(o) ? [] : {} hash.set(o, obj) for (let i in o) { if (isObject(o[i])) { obj[i] = deepClone4(o[i], hash) } else { obj[i] = o[i] } } return obj } // 递归易出现爆栈问题 // 将递归改为循环,就不会出现爆栈问题了 var a1 = { a: 1, b: 2, c: { c1: 3, c2: { c21: 4, c22: 5 } }, d: 'asd' }; var b1 = { b: { c: { d: 1 } } } function cloneLoop(x) { const root = {}; // 栈 const loopList = [ //->[]->[{parent:{a:1,b:2},key:c,data:{ c1: 3, c2: { c21: 4, c22: 5 } }}] { parent: root, key: undefined, data: x, } ]; while (loopList.length) { // 深度优先 const node = loopList.pop(); const parent = node.parent; //{} //{a:1,b:2} const key = node.key; //undefined //c const data = node.data; //{ a: 1, b: 2, c: { c1: 3, c2: { c21: 4, c22: 5 } }, d: 'asd' } //{ c1: 3, c2: { c21: 4, c22: 5 } }} // 初始化赋值目标,key 为 undefined 则拷贝到父元素,否则拷贝到子元素 let res = parent; //{}->{a:1,b:2,d:'asd'} //{a:1,b:2}->{} if (typeof key !== 'undefined') { res = parent[key] = {}; } for (let k in data) { if (data.hasOwnProperty(k)) { if (typeof data[k] === 'object') { // 下一次循环 loopList.push({ parent: res, key: k, data: data[k], }) } else { res[k] = data[k]; } } } } return root } function deepClone5(o) { let result = {} let loopList = [ { parent: result, key: undefined, data: o } ] while (loopList.length) { let node = loopList.pop() let { parent, key, data } = node let anoPar = parent if (typeof key !== 'undefined') { anoPar = parent[key] = {} } for (let i in data) { if (typeof data[i] === 'object') { loopList.push({ parent: anoPar, key: i, data: data[i] }) } else { anoPar[i] = data[i] } } } return result } let cloneA1 = deepClone5(a1) cloneA1.c.c2.c22 = 5555555 console.log(a1); console.log(cloneA1); // ------------------------------------------JSON.stringify()实现深拷贝 function cloneJson(o) { return JSON.parse(JSON.stringify(o)) } // let obj = { a: { c: 1 }, b: {} }; // obj.b = obj; // console.log(JSON.parse(JSON.stringify(obj))) // 报错 // Converting circular structure to JSON
深拷贝能使用hash递归的方式写出来就可以了
不过技多不压身,推荐还是看一看使用while实现深拷贝方法
答:因为JS里面有可视的Dom,如果是多线程的话,这个线程正在删除DOM节点,另一个线程正在编辑Dom节点,导致浏览器不知道该听谁的
class MyPromise2 { constructor(executor) { // 规定状态 this.state = "pending" // 保存 `resolve(res)` 的res值 this.value = undefined // 保存 `reject(err)` 的err值 this.reason = undefined // 成功存放的数组 this.successCB = [] // 失败存放的数组 this.failCB = [] let resolve = (value) => { if (this.state === "pending") { this.state = "fulfilled" this.value = value this.successCB.forEach(f => f()) } } let reject = (reason) => { if (this.state === "pending") { this.state = "rejected" this.value = value this.failCB.forEach(f => f()) } } try { // 执行 executor(resolve, reject) } catch (error) { // 若出错,直接调用reject reject(error) } } then(onFulfilled, onRejected) { if (this.state === "fulfilled") { onFulfilled(this.value) } if (this.state === "rejected") { onRejected(this.value) } if (this.state === "pending") { this.successCB.push(() => { onFulfilled(this.value) }) this.failCB.push(() => { onRejected(this.reason) }) } } } Promise.all = function (promises) { let list = [] let count = 0 function handle(i, data) { list[i] = data count++ if (count == promises.length) { resolve(list) } } return Promise((resolve, reject) => { for (let i = 0; i < promises.length; i++) { promises[i].then(res => { handle(i, res) }, err => reject(err)) } }) } Promise.race = function (promises) { return Promise((resolve, reject) => { for (let i = 0; i < promises.length; i++) { promises[i].then(res => { resolve(res) }, err => { reject(err) }) } }) }
async function async1() { console.log('async1 start') await async2() console.log('async1 end') } async function async2() { console.log('async2') } async1() console.log('script start') //执行到await时,如果返回的不是一个promise对象,await会阻塞下面代码(当前async代码块的代码),会先执行async外的同步代码(在这之前先看看await中函数的同步代码,先把同步代码执行完),等待同步代码执行完之后,再回到async内部继续执行 //执行到await时,如果返回的是一个promise对象,await会阻塞下面代码(当前async代码块的代码),会先执行async外的同步代码(在这之前先看看await中函数的同步代码,先把同步代码执行完),等待同步代码执行完之后,再回到async内部等promise状态达到fulfill的时候再继续执行下面的代码 //所以结果为 //async1 start //async2 //script start //async1 end
答:
script
、setTimeOut
、setInterval
、setImmediate
promise.then
,process.nextTick
、Object.observe
、MutationObserver
答:
例题1
setTimeout(function(){ console.log('1') }); new Promise(function(resolve){ console.log('2'); resolve(); }).then(function(){ console.log('3') }); console.log('4'); new Promise(function(resolve){ console.log('5'); resolve(); }).then(function(){ console.log('6') }); setTimeout(function(){ console.log('7') }); function bar(){ console.log('8') foo() } function foo(){ console.log('9') } console.log('10') bar()
解析
例题2
setTimeout(() => { console.log('1'); new Promise(function (resolve, reject) { console.log('2'); setTimeout(() => { console.log('3'); }, 0); resolve(); }).then(function () { console.log('4') }) }, 0); console.log('5'); //5 7 10 8 1 2 4 6 3 setTimeout(() => { console.log('6'); }, 0); new Promise(function (resolve, reject) { console.log('7'); // reject(); resolve(); }).then(function () { console.log('8') }).catch(function () { console.log('9') }) console.log('10');
运行结果: 5 7 10 8 1 2 4 6 3
答:
undefined
答:
var
let
const
exports
和module.exports
有什么区别?⭐⭐⭐exports.xxx='xxx'
module.export = {}
exports
是module.exports
的引用,两个指向的是用一个地址,而require能看到的只有module.exports
Commonjs、AMD、CMD、UMD、ESM 都有什么区别
Commonjs
module.exports = xxx
require('xxx')
AMD/CMD/UMD 适用前端 异步执行
define(["a","b","c","d","e"],function(a,b,c,d,e){
// 相当于在前面声明并初始化了要用到的所有模块
a.dosomething()
if(false) {
// 即使没有用到模块 b,也会提前执行
b.dosomething()
}
})
define(function(require, exports, module){
var a = require("./a") //需要的时候声明
a.dosomething()
if(false) {
var b = require("./b")
b.dosomething()
}
})
ESM
ESM 和 commonjs 的区别主要在于
arguments
对象,不能使用arguments
,如果要获取参数的话可以使用rest
运算符yield
属性,不能作为生成器Generator使用_proto_
指向函数的prototypevar Ajax = { get: function (url, callback) { let xhr = XMLHttpRequest(); xhr.open("get", url, false) xhr.onreadystatechange = function () { if (xhr.readyState == 4) { if (xhr.status == 200 || xhr.status == 304) { console.log(xhr.responseText); callback(xhr.responseText) } } } xhr.send() }, post: function (url, data, callback) { let xhr = new XMLHttpRequest() // 第三个参数为是否异步执行 xhr.open('post', url, true) // 添加http头,设置编码类型 xhr.setRequestHeader("Content-type","x-www-form-urlencoded") xhr.onreadystatechange = function () { if(xhr.readyState == 4) { if(xhr.status == 200 || xhr.status == 304) { console.log(xhr.responseText); callback(xhr.responseText) } } } xhr.setRequestHeader('Content-type', "application/x-www-urlencoded") xhr.send(data) } }
// ---------------------------------------------------------防抖函数 function debounce(func, delay) { let timeout return function () { let arg = arguments if (timeout) clearTimeout(timeout) timeout = setTimeout(() => { func(arg) }, delay); } } // ---------------------------------------------------------立即执行防抖函数 function debounce2(fn, delay) { let timer return function () { let args = arguments if (timer) clearTimeout(timer) let callNow = !timer timer = setTimeout(() => { timer = null }, delay); if (callNow) { fn(args) } } } // ---------------------------------------------------------立即执行防抖函数+普通防抖 function debounce3(fn, delay, immediate) { let timer return function () { let args = arguments let _this = this if (timer) clearTimeout(timer) if (immediate) { let callNow = !timer timer = setTimeout(() => { timer = null }, delay); if (callNow) { fn.apply(_this, args) } } else { timeout = setTimeout(() => { func.apply(_this, arguments) }, delay); } } } // ---------------------------------------------------------节流 ,时间戳版 function throttle(fn, wait) { let previous = 0 return function () { let now = Date.now() let _this = this let args = arguments if (now - previous > wait) { fn.apply(_this, arguments) previous = now } } } // ---------------------------------------------------------节流 ,定时器版 function throttle2(fn, wait) { let timer return function () { let _this = this let args = arguments if (!timer) { timer = setTimeout(() => { timer = null fn.apply(_this, arguments) }, wait); } } }
function add() { var args = Array.prototype.slice.call(arguments) var adder = function () { args.push(...arguments) return adder } adder.toString = function () { return args.reduce((prev, curr) => { return prev + curr }, 0) } return adder } let a = add(1, 2, 3) let b = add(1)(2)(3) console.log(a) console.log(b) console.log(add(1, 2)(3)); console.log(Function.toString) // --------普通函数转为柯里化函数------ function createCurry(fn, args = []) { return function () { let _args = args.concat(...arguments) if (_args.length < fn.length) { return createCurry.call(this, fn, _args) } return fn.apply(this, _args) } } function add(a, b, c) { return a + b + c; } var _add = createCurry(add); console.log(_add(1, 2, 3)); console.log(_add(1)(2, 3)); console.log(_add(1)(2)(3));
var s = 0
function f() {
s++
console.log(s);
if (s < 999) {
window.requestAnimationFrame(f)
}
}
window.requestAnimationFrame(f)
var Single = (function () { var instance = null function Single(name) { this.name = name } return function (name) { if (!instance) { instance = new Single(name) } return instance } })() var oA = new Single('hi') var oB = new Single('hello') console.log(oA); console.log(oB); console.log(oB === oA);
function Animal(o) {
var instance = new Object()
instance.name = o.name
instance.age = o.age
instance.getAnimal = function () {
return "name:" + instance.name + " age:" + instance.age
}
return instance
}
var cat = Animal({name:"cat", age:3})
console.log(cat);
class Watcher { // name模拟使用属性的地方 constructor(name, cb) { this.name = name this.cb = cb } update() {//更新 console.log(this.name + "更新了"); this.cb() //做出更新回调 } } class Dep {//依赖收集器 constructor() { this.subs = [] } addSubs(watcher) { this.subs.push(watcher) } notify() {//通知每一个观察者做出更新 this.subs.forEach(w => { w.update() }); } } // 假如现在用到age的有三个地方 var w1 = new Watcher("我{{age}}了", () => { console.log("更新age"); }) var w2 = new Watcher("v-model:age", () => { console.log("更新age"); }) var w3 = new Watcher("I am {{age}} years old", () => { console.log("更新age"); }) var dep = new Dep() dep.addSubs(w1) dep.addSubs(w2) dep.addSubs(w3) // 在Object.defineProperty 中的 set中运行 dep.notify()
//封装函数,处理延迟300ms问题 function tap(obj, callback) { var isMove = false; var startTime = 0; obj.addEventListener('touchstart', function () { startTime += Date.now(); }) obj.addEventListener('touchmove', function () { isMove = true; }) obj.addEventListener('touchend', function () { if (!isMove && (Date.now() - startTime) < 150) { callback && callback(); } isMove = false; startTime = 0; }) }
input type = 'file’去接收
用 window.URL.createObjectURL(file)把 file 文件转换为 URL(现场的/前端转为 URL)
或者用 FormData 去接收, 把 file 文件append 进去,然后传给后端,使用返回的 URL
可以作为参数传给函数,一般用于 for 循环赋值
暂时性死区是指,当进入一个作用域,我去使用一个变量名,而这个变量名已经存在了,但是是不可获取的,就会报错,造成暂时性死区问题;比如一个作用域下面使用了 let 定义了 x
,但是在定义之前就使用了 x
,就会报错;暂时性死区意味着 typeof 也不是绝对安全的操作
x = '123'; // 报错
let x = 1
---------------------
typeof y; // 报错
let y = 123
reduce、map、filter、every、some、foreach.
Push、pop、shift、unshift、splice、sort、reverse
不改变的
join 变成字符
Slice,截取
concat 合并数组
foreach 没有返回值,一般如果用来遍历修改原数组的话可以用 foreach 方法
window.onbeforeunload = function (e) {
e = e || window.event;
// 兼容IE8和Firefox 4之前的版本
if (e) {
e.returnValue = '关闭提示';
}
// Chrome, Safari, Firefox 4+, Opera 12+ , IE 9+
return '关闭提示';
};
创建一个canvas对象,把图片保存在 canvas 中,然后 canvas 对象 toDataUrl,在把 dataurl 数据存储在 localstorage 中。
或者使用 blob 二进制流存储,canvas 对象toBlob
使用 input 接受大文件,使用file.slice进行分割分块上传(制定好一个块的大小,然后进行分割),等所有块上传完毕之后,promise.all(),运行成功回调
js是单线程的,而web worker可以多创建一个子线程,多出来的这个子线程执行代码时不会阻塞主线程。它有几个限制,
同源限制,子线程资源必须和主线程资源是同源
dom限制,子线程不能操作dom
文件限制,不能打开本机(file://)文件,只能来源于网络
通信限制,只能使用postmessage来传输信息
脚本限制,不能使用alert、confirm方法
let fun = { fun1: function() { console.log("fun1"); return this; }, fun2: function() { console.log("fun2"); return this; }, fun3: function() { console.log("fun3"); return this; } } fun.fun1().fun2().fun3();
浏览器和 Node 环境下,microtask 任务队列的执行时机不同
Node 端,microtask 在事件循环的各个阶段之间执行
浏览器端,microtask 在事件循环的 macrotask 执行完之后执行
https://zhuanlan.zhihu.com/p/54882306
顾名思义,reflect反射的意思。可以反射对象
Reflect可以提供一些方法去拦截js的操作,Reflect不是一个函数对象,所以它不可构造,Reflect内部的方法和属性都是静态的。
比如创建一个没有原型的对象,也就是说他自己不能调用任何基于Object原型链上的方法
var myObject = Object.create(null)
// 如果想列举它的key值,只需使用Reflect的静态方法,拦截该对象,然后做出处理
Reflect.ownKeys(myObject)
Object.keys只列出非原型上可枚举的key值,而Object.getOwnPropertyNames列出非原型上的所有key值(Symbol除外)
//rem适配
(function () {
const styleEle = document.createElement('style');
const docWidth = document.documentElement.clientWidth;
const rootFontSize = docWidth / 16;
styleEle.innerHTML = 'html{font-size:' + rootFontSize + 'px!important}';
document.head.appendChild(styleEle);
})()
触底加载
bom就是window,包含windows(窗口)、navigator(浏览器)、screen(浏览器屏幕)、history(访问历史)、location(地址)等,浏览器相关的东西。bom是包含dom的。
dom是document, html相关的都在里面
e.target是点击的那个对象,e.currentTarget是绑定该事件的对象
function foo() {
console.log(this.a)
}
var a = 1
foo() //1
const obj = { a: 2, foo: foo }
obj.foo() //2
const c = new foo() //undefined
说完了以上几种情况,其实很多代码中的 this 应该就没什么问题了,下面看看箭头函数中的 this
function a() {
return () => {
return () => {
console.log(this)
}
}
}
a()()() //Window
this 关键词指的是它所属的对象。
它拥有不同的值,具体取决于它的使用位置:
let 特点:
const 特点:
定义:ES6允许按照一定的模式从数组和对象中提取值,对变量进行赋值,称为解构赋值。
数组解构
const arr = ['张学友', '刘德华', '黎明', '郭富城'];
let [zhang, liu, li, guo] = arr;
对象解构
const lin = { name: '林志颖', tags: ['车手', '歌手', '小旋风', '演员'] }; let {name, tags} = lin; // 复杂解构 let wangfei = { name: '王菲', age: 18, songs: ['红豆', '流年', '暧昧', '传奇'], history: [ {name: '窦唯'}, {name: '李亚鹏'}, {name: '谢霆锋'} ] }; let {songs: [one, two, three], history: [first, second, third]} = wangfei;
ES6 引入新的声明字符串的方式 『 `` 』 ‘’ “”
语法:${ 内容 }
作用:
ES6 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法。
let name = '尚硅谷';
let change = function(){
console.log('我们可以改变你!!');
}
const school = {
name, // name : name, 简化
change,
improve(){ // improve: function(){ 简化如下
console.log("我们可以提高你的技能");
}
}
console.log(school);
语法: () => { }
//ES6 允许给函数参数赋值初始值 //1. 形参初始值 具有默认值的参数, 一般位置要靠后(潜规则) function add(a,b,c=10) { return a + b + c; } let result = add(1,2); console.log(result); //2. 与解构赋值结合 function connect({host="127.0.0.1", username,password, port}){ console.log(host) console.log(username) console.log(password) console.log(port) } connect({ host: 'atguigu.com', // 若无参数传递,将使用默认值 username: 'root', password: 'root', port: 3306 })
ES6 引入 rest 参数,用于获取函数的实参,用来代替 arguments
// ES5 获取实参的方式 function date(){ console.log(arguments); } date('白芷','阿娇','思慧'); // rest 参数 function date(...args){ console.log(args);// 数组 filter some every map } date('阿娇','柏芝','思慧'); // rest 参数必须要放到参数最后 function fn(a,b,...args){ console.log(a); console.log(b); console.log(args); } fn(1,2,3,4,5,6);
『 … 』 扩展运算符能将『数组』转换为逗号分隔的『参数序列』
//声明一个数组 ... const tfboys = ['易烊千玺','王源','王俊凯']; // ...tfboys => '易烊千玺','王源','王俊凯' // 声明一个函数 function chunwan(){ console.log(arguments); } chunwan(...tfboys);// chunwan('易烊千玺','王源','王俊凯') // 应用 //1. 数组的合并 const kuaizi = ['王太利','肖央']; const fenghuang = ['曾毅','玲花']; // const zuixuanxiaopingguo = kuaizi.concat(fenghuang); const zuixuanxiaopingguo = [...kuaizi, ...fenghuang]; console.log(zuixuanxiaopingguo); //2. 数组的克隆 const sanzhihua = ['E','G','M']; const sanyecao = [...sanzhihua];// ['E','G','M'] console.log(sanyecao); //3. 将伪数组转为真正的数组 const divs = document.querySelectorAll('div'); const divArr = [...divs]; console.log(divArr);
ES6 引入了一种新的原始数据类型 Symbol,表示独一无二的值。它是JavaScript 语言的第七种数据类型,是一种类似于字符串的数据类型。
其中数据类型:undefined、string、symbol、object、null、number、boolean
//创建Symbol let s = Symbol(); console.log(s, typeof s); let s2 = Symbol('尚硅谷'); let s3 = Symbol('尚硅谷'); console.log(s2 === s3); // false //Symbol.for 创建 let s4 = Symbol.for('尚硅谷'); let s5 = Symbol.for('尚硅谷'); console.log(s4 === s5); // true //不能与其他数据进行运算 let result = s + 100; let result = s > 100; let result = s + s; 注:和 Symbol()不同的是,用Symbol.for()方法创建的的 symbol 会被放入一个全局 symbol 注册表中。 Symbol.for() 并不是每次都会创建一个新的 symbol,它会首先检查给定的 key 是否已经在注册表中了。假 如是,则会直接返回上次存储的那个。否则,它会再新建一个。
关于Symbol的更多信息@点击了解更多
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。