闭包
是指存在自由变量的函数!
在A作用域中使用的变量a,却没有在A作用域中声明(即在其他作用域中声明的),对于A作用域来说,a就是一个自由变量。如下代码
案例1
var a = 10
function fn(){
var b = 20
console.log(a + b)
}
案例2
function a(x, y){
console.log(x, y) //在这里,x和y都不是自由变量
function b(){
console.log(x, y) //但在这个内部函数b中,x和y相对于b都是自由变量,而函数a的作用域则是环境。
}
//无论b最终是否会作为返回值被函数a返回,b本身都已经形成了闭包。
}
案例3
函数可以记住并访问所在的词法作用域
function a () {
let value = 2;
function b () {
return value += 2; // 对value局部变量进行累加
}
return b;
}
const fn = a();
console.log(fn()); // 4
console.log(fn()); // 6
console.log(fn()); // 8
优点:1、避免全局变量污染。2、缓存变量
缺点:容易造成内存泄漏
promise
是一个构造函数,是用来处理异步操作的解决方式promise构造函数
是同步执行的,then方法是异步执行的new Promise(resolve=>{
console.log(1);
resolve(3);
}).then(res => {
console.log(res);
});
console.log(2);
// 输出结果 1, 2, 3
const p = Promise.all([p1, p2, p3]);
finally()
方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法基本数据类型
和引用数据类型
let a = 10
let a2 = '10'
let a3 = null
let a4 = undefined
let a5 = true
let a6 = {id:1,value:'20'}
let a7 = [1,2,3,4,5,6]
let a8 = function(){
return '6'
}
console.log(typeof(a)) //number
console.log(typeof(a2)) //string
console.log(typeof(a3)) //object
console.log(typeof(a4)) //undefined
console.log(typeof(a5)) //boolean
console.log(typeof(a6)) //object
console.log(typeof(a7)) //object
console.log(typeof(a8)) //function
浅拷贝
:只复制指向某个对象的指针,而不复制这个对象本身,新旧对象共享一块内存。深拷贝
:复制并创建一个一模一样的对象,不共享内存,修改新对象旧对象不会变。深拷贝方式:
1、递归(对可遍历的属性进行递归)
// 一般来说,在js中我们用JSON.parse(JSON.stringify(obj))来完成深拷贝,但是该方法不能解决属性为函数,undefined,循环引用的的情况,因此我们有时需要自己实现一个深拷贝。
function clone(o) {
var temp = {}
for (var key in o) {
if (typeof o[key] === 'object' && o[key] !== null) {
temp[key] = clone(o[key])
} else {
temp[key] = o[key]
}
}
return temp
}
2、使用JSON对象的parse和stringify
浅拷贝方式:
call(thisObj, arg, arg2, arg3);
apply(thisObj, [args]);
bind(thisObj,arg, arg2, arg3)(); //bind 返回的是一个新的函数,必须调用它才会被执行。
Call
let p1 = {
firstName: 'John',
lastName: 'Smith'
};
let p2 = {
firstName: 'Ann',
lastName: 'Brown'
};
function sayWelcome(greeting) {
console.log(`${greeting} ${this.firstName} ${this.lastName}`);
}
sayWelcome.call(p1, 'Welcome'); // Welcome John Smith
sayWelcome.call(p2, 'Welcome'); // Welcome Ann Brown
Apply
let p1 = {
firstName: 'John',
lastName: 'Smith'
};
let p2 = {
firstName: 'Ann',
lastName: 'Brown'
};
function sayWelcome(greeting) {
console.log(`${greeting} ${this.firstName} ${this.lastName}`);
}
sayWelcome.apply(p1, ['Welcome']); // Welcome John Smith
sayWelcome.apply(p2, ['Welcome']); // Welcome Ann Brown
Bind
let p1 = {
firstName: 'John',
lastName: 'Smith'
};
let p2 = {
firstName: 'Ann',
lastName: 'Brown'
};
function sayWelcome(greeting,greeting2) {
console.log(`${greeting} ${this.firstName} ${this.lastName}`)
}
sayWelcome.bind(p1, 'Welcome')(); // Welcome John Smith
sayWelcome.bind(p2, 'Welcome')(); // Welcome Ann Brown
1、原型对象(Person.prototype)是 构造函数(Person)的一个实例。
2、JavaScript是⾯向对象的,每个实例对象都有⼀个__proto__属性,该属性指向它的原型对象,这个实例对象的构造函数有⼀个原型属性 prototype,与实例的proto属性指向同⼀个对象。当⼀个对象在查找⼀个属性的时候,⾃⾝没有就会根据__proto__向它的原型进⾏查找,如果 都没有,则向它的原型的原型继续查找,直到查到Object.prototype.proto_为null,这样也就形成了原型链。
原型链的顶层就是Object.prototype,而这个对象的是没有原型对象的。
在判断一个变量是否存在时应该用typeof来判断,如if(typeof a!="undefined"){},而不要去使用if(a)因为如果a不存在(未声明)则会出错,
if(typeof a !== 'undefined'){
console.log(10)
}
//运行结果不报错
if(a){
console.log(10)
}
//运行结果报 a is not defined 错
const arr = [1, 2, 3, 4, 5, 6]
console.log(arr instanceof Array) //true
console.log(typeof arr) // object
instanceof
preventDefault()
函数: a标签,radio、checkbox复选框等表单元素,div没有默认事件
<input type="checkbox" id="checkbox"/>
<a href="http://www.good1230.com/" target="_black">HLJ的博客</a>
<script>
let dom = document.querySelector("#checkbox")
dom.addEventListener("click", function(event) {
console.log("preventDefault() 阻止了复选框选择事件!")
event.preventDefault();
}, false);
let dom2 = document.querySelector("a");
dom2.addEventListener("click",function(e){
console.log("preventDefault() 阻止了a标签跳转事件!")
e.preventDefault()
},false);
</script>
stopPropagation()
函数
<div id="c" onclick="alert(1)">
<div id="c2" onclick="alert(2)">
<input type="button" id="c3" value="点击" onclick="alert(3)">
</div>
</div>
点击button按钮时,浏览器会先后弹出3, 2, 1,这时我们可以用stopPropagation()来阻止触发两个div上的事件
let dom = document.getElementById('c3')
dom.addEventListener('click',function(e){
e.stopPropagation()
},false);
createElement()
//创建元素标签let element = document.createElement('div')
document.body.appendChild(element)
createTextNode()
// 创建一个文本节点let element = document.createElement('div')
let elementNode = document.createTextNode('text')
element.appendChild(elementNode)
document.body.appendChild(element)
appendChild()
// 添加标签let element = document.createElement('div')
document.body.appendChild(element)
removeChild()
// 删除标签let element = document.getElementById("div")
document.body.removeChild(element)
insertBefore()
// 插入标签<ul id="myList">
<li>a</li>
<li>c</li>
</ul>
<button onclick="myFunction()">试一下</button>
<script>
function myFunction()
{
let newElement=document.createElement("li")
let newElementNode=document.createTextNode("b")
newElement.appendChild(newElementNode)
let list=document.getElementById("myList")
list.insertBefore(newElement,list.childNodes[0]);
}
</script>
insertBefore()
// 更新标签localStorage
:
localStorage.setItem(key, value)
localStorage.getItem(key)
localStorage.removeItem(key)
localStorage.clear()
sessionStorage
:
sessionStorage.setItem(key, value)
sessionStorage.getItem(key)
sessionStorage.removeItem(key)
sessionStorage.clear()
特性 | LocalStorage | SessionStorage | Cookie |
---|---|---|---|
存放数据大小 | LocalStorage 的存储容量为5MB/10MB | SessionStorage 的存储容量为5MB | Cookies的存储容量为4KB |
数据生命周期 | 由于它不是基于会话的,因此必须通过 javascript 或手动删除 | 它是基于会话的,可以按窗口或选项卡工作。这意味着数据仅在会话期间存储,即直到浏览器(或选项卡)关闭 | Cookies 根据选项卡和窗口的设置和工作过期 |
客户端只能读取本地存储 | 客户端只能读取本地存储 | 客户端和服务器都可以读取和写入 cookie | |
没有向服务器传输数据 | 没有向服务器传输数据 | 存在到服务器的数据传输 | |
浏览器兼容性 | 支持它的旧浏览器较少 | 支持它的旧浏览器较少 | 所有浏览器都支持它,包括旧版浏览器 |
共同点 | 都是保存在浏览器端 | 都是保存在浏览器端 | 都是保存在浏览器端 |
特性 | get | post |
---|---|---|
参数位置 | 参数拼接到URL后面 | 参数在请求体中 |
参数大小 | 受限于浏览器url大小,一般不超过32K | 1G |
安全性 | 参数暴露在URL中,安全性低 | 相对get安全性更高些 |
适用场景 | 从服务器端获取数据 | 从服务器端获取数据 |
缓存 | 在浏览器中可缓存 | 不可以缓存 |
GET 和 POST 其实都是 HTTP 的请求方法。除了这 2 个请求方法之外,HTTP 还有 HEAD、PUT、DELETE、TRACE、CONNECT、OPTIONS 这 6 个请求方法。所以HTTP 的请求方法共计有 8 种。
let array = ['1','2',3].map(parseInt)
console.log(array)
// 输出结果 [1, NaN, NaN]
parseInt() 函数可解析一个字符串,并返回一个整数。
parseInt(string, radix)
参数 | 描述 |
---|---|
string | 必需。要被解析的字符串。 |
radix | 1、可选。表示要解析的数字的基数。该值介于 2 ~ 36 之间。2、如果省略该参数或其值为 0,则数字将以 10 为基础来解析。如果它以 “0x” 或 “0X” 开头,将以 16 为基数。3、如果该参数小于 2 或者大于 36,则 parseInt() 将返回 NaN。 |
当参数 radix 的值为 0,或没有设置该参数时,parseInt() 会根据 string 来判断数字的基数。
举例,如果 string 以 "0x" 开头,parseInt() 会把 string 的其余部分解析为十六进制的整数。如果 string 以 0 开头,那么 ECMAScript v3 允许 parseInt() 的一个实现把其后的字符解析为八进制或十六进制的数字。如果 string 以 1 ~ 9 的数字开头,parseInt() 将把它解析为十进制的整数。
parseInt("10"); //返回 10
parseInt("19",10); //返回 19 (10+9)
parseInt("11",2); //返回 3 (2+1)
parseInt("17",8); //返回 15 (8+7)
parseInt("1f",16); //返回 31 (16+15)
parseInt("010"); //未定:返回 10 或 8
map返回一个新的数组,forEach返回undefined
使用场景:修改或返回新数组使用map,相反只是做遍历循环时用foreach 或 for
const array = [1, 2, 3, 4, 5]
const arr = array.forEach(x => x * x)
const arr2 = array.map(x => x * x)
console.log(array) // [1, 2, 3, 4, 5]
console.log(arr) // undefined
console.log(arr2) //[1, 4, 9, 16, 25]
% – %单位用于设置相对于当前字体大小的字体大小。
em -相对于当前对象内文本的字体尺寸(参考物是父元素的font-size)
注意:这里的 2em 表示当前字体大小的 2 倍。
rem – rem是相对于HTML根元素的字体大小(font-size)来计算的长度单位
px -相对于显示器屏幕分辨率
vh - 相对于视口高度的 1%。
vw – 相对于视口宽度的 1%
js 是单线程执行的,js中的任务按顺序一个一个的执行,但是一个任务耗时太长; 那么后面的任务就需要等待,为了解决这种情况,将任务分为了同步任务和异步任务; 而异步任务又可以分为微任务和宏任务。
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0);
Promise.resolve().then(function() {
console.log('promise1');
}).then(function() {
console.log('promise2');
});
console.log('script end');
返回结果的打印顺序是:
script start
script end
promise1
promise2
setTimeout
var r = new Promise(function(resolve, reject){
console.log("a");
resolve()
});
setTimeout(()=>console.log("d"), 0)
r.then(() => console.log("c"));
console.log("b")
再回头来看看开头的一段代码,会不会豁然开朗了呢。JS 引擎首先会把Promise对象 和 console.log(“b”) 两个微观任务存入执行栈,把 setTimeout(宏观任务)存入 “任务队列”
所以在输出 a 和 b 以后并不会按照预期那样立即从 “任务队列” 中读取 setTimeout,因为 then方法是微观任务Promise对象的回调函数,先于 setTimeout 执行
Promise.resolve().then(()=>{
console.log('1')
setTimeout(()=>{
console.log('2')
},0)
})
setTimeout(()=>{
console.log('3')
Promise.resolve().then(()=>{
console.log('4')
})
},0)
打印1 3 4 2
在交流群中看到有的小伙伴还是不太清楚正确的执行顺序,基于前面的介绍,大致的分析过程及草图如下:
1(红色):JS 引擎会把微观任务Promise存入执行栈,把宏观任务setTimeout存入 “任务队列”
2(绿色):主线程率先运行执行栈中的代码,依次输入1,然后把绿框的setTimeout存入 “任务队列”
3(蓝色):执行栈清空以后,会率先读取 “任务队列” 中最早存入的setTimeout(红框的那个),并把这个定时器存入栈中,开始执行。这个定时器中的代码都是微观任务,所以可以一次性执行,依次输出3 和 4
4(紫色):重复第3步的操作,读取 “任务队列” 中最后存入的setTimeout(绿框的那个),输出2
所以最终的输出结果就是 1 3 4 2
Promise.resolve().then(()=>{
console.log('1')
setTimeout(()=>{
console.log('2')
},0)
})
setTimeout(()=>{
console.log('3')
Promise.resolve().then(()=>{
console.log('4')
})
}, 3000)
打印1 2 3 4
setTimeout(function(){console.log(4)},0);
new Promise(function(resolve){
console.log(1)
for( var i=0 ; i<10000 ; i++ ){
i==9999 && resolve()
}
console.log(2)
}).then(function(){
console.log(5)
});
console.log(3);
另外一个会让人感到迷惑的地方就是 resolve回调函数内部的那几行代码,输出1以后接着跑1000次循环才调用resolve方法,其实resolve()的意思是把 Promise对象实例的状态从pending变成 fulfilled(即成功)
成功的回调就是对应的then方法。所以resolve() 后面的 console.log(2) 会先执行,因为 resolve() 回调函数是在本轮事件循环的末尾执行 (关于这部分内容,可以参考 Promise对象 一文)
打印1 2 3 5 4
setTimeout(function(){console.log(4)},0);
new Promise(function(resolve){
console.log(1)
for( var i=0 ; i<10000 ; i++ ){
// i==9999 && resolve()
}
console.log(2)
}).then(function(){
console.log(5)
});
console.log(3);
打印1 2 3 4
https://dandelioncloud.cn/article/details/1470389873935970306/