立即执行函数&作用域:点击第几个弹出几
// 有问题的代码:
var list = document.getElementById("list");
var li = list.children;
for(var i = 0 ;i<li.length;i++){
li[i].onclick=function(){
alert(i); // 结果总是3.而不是0,1,2
}
}
// 方案1:立即执行函数
var list = document.getElementById("list");
var li = list.children;
for(var i = 0 ;i<li.length;i++){
(function(j) {li[j].onclick=function(){
alert(j); // 结果是0,1,2
}})(i)
}
// 方案2:let 块级作用域
var list = document.getElementById("list");
var li = list.children;
for(let i = 0 ;i<li.length;i++){
li[i].onclick=function(){
alert(i); // 结果是0,1,2
}
}
for...in VS for...of
- for...in包含对象,获取到的是下标或者键名。for...of不能用于对象,拿到的是元素值。
- 可遍历对象的例如: for...in, Object.keys/values/entires
null VS undefined
- null:是object的一个特殊值,表示对象不是有效的对象,是一个不存在的对象的占位符。
- undefined:声明了变量,但是没有给变量赋值,或者访问对象上不存在的属性
- typeof null === 'object' / typeof undefined === 'undefined'
- Number(null) ==> 0 / Number(undefined) ==> NaN
instanceof VS typeof
- typeof可检测 number、string、boolean、function、undefined、object。
- instanceof是对原型链进行判断,可以分别array和object
原型链
事件循环、宏任务、微任务
递归
// 阶乘
function factorial(num){
return num == 1 ? 1 : num * factorial( -- num )
}
factorial(5) // 120
// 累加
function sum(num){
return num == 1 ? 1 : num + sum(num - 1)
}
sum(10) // 55
// 递归实现类似reduce求和
function sum( ...args ){
return args.length == 0 ? 0 : args.pop() + sum( ...args)
};
sum( 1,3,5,7,9 ); // 25
this指向问题
- this是一个指针型变量,它动态指向当前函数的运行环境
- 他永远指向所在函数的真实调用者,如果没有调用者,就指向全局window
- 箭头函数中的this,指向的是函数定义位置的上下文this
- 如果有new关键字,this指向new出来的那个函数
- 普通函数、匿名函数、立即执行函数、回调函数的this都是指向window
- 对象下的函数,谁调用就指向谁
- dom回调,this指向绑定事件的对象。
改变this指向的三种方法call、bind、apply
-
共同点
- 三个函数的第一个参数都是改变this的指针
-
不同点
- call和apply都会自动执行,bind不会立刻指向,需要手动调用一次,有两次传参的机会(可以bind时候传入参数,也可以在调用的时候传入参数。如果都有参数,则以先传入的为主,按顺位取参)
- call和bind都可以接收多个参数,apply只接收两个参数,第二个参数是一个数组。
-
代码示例
// 用=改变dom事件中的this指向
<body>
<button>hd</button>
<button>cms</button>
</body>
<script>
function show(){
alert(this.innerHTML)
}
let btns = document.querySelectorAll('button');
for(var i = 0; i< btns.length; i ++){
btns[i].addEventListener('click', (event)=>{
// call 改变this指向,将show的this指向成event.target
// call apply 都会立即执行,bind不会立即执行,要手动调一次
// call bind都可以有多个入参,apply的入参只有两个,第二个是数组
show.call(event.target)
})
}
</script>
// 用Math取数组中最大值
let arr = [1,4,3,5,3,2];
// let max = Math.max(...arr);
// Math.max本身并不支持数组,调用apply将数组作为参数,打散后,传给Math.max。这样参数就从数组变成了打散之后的数据了。目的就是打散数组,传给Math.max,所以不关注this指向,可以指定为null。
let max = Math.max.apply(null, arr) // apply第二个入参为数组,用bind、call的话,要将arr打散。
console.log( max );
// 构造函数的方法继承
function Request(){
this.get = function(params){
let str = Object.keys(params).map( key => `${ key }=${params[key]}`).join('&');
return `https://${this.url}?${str}`
}
}
function Article(){
this.url = 'articles/list';
// bind 不会立即执行,如果是要用bind的话,就需要手动执行一次。用call或者apply则不需要。
// 此处 执行Request函数,往this中追加一个get属性,后边在访问属性的时候,才可以拿到。
Request.bind(this)();
}
function News(){
this.url = 'articles/list';
Request.call(this);
}
let a = new Article();
let n = new News();
console.log( n.get({ id: 2, cat: 'news' }));
console.log( a.get({ id: 1, cat: 'js'}) );
// bind不会立刻执行,例如代码所举例所用,在点击的时候才会执行
<body>
<button>hd</button>
</body>
<script>
let btn = document.querySelector('button');
btn.addEventListener('click', function(event){
console.log( this.url + event.target.innerHTML ); // baidu.comhd
}.bind({ url: 'baidu.com' }))
</script>
// 利用bind随机变化dom的背景颜色
<body>
<button>hd</button>
</body>
<script>
function Color(elem){
this.elem = elem;
this.colors = ['#0189ff', '#04899da', '#ff3322'];
this.run = function(){
setInterval(function(){
let i = Math.floor(Math.random()* this.colors.length);
this.elem.style.background = this.colors[i]
}.bind(this), 1000)
}
}
let c = new Color(document.body);
c.run();
</script>
闭包
定义:闭包就是能够读取其他函数内部变量的函数。
作用:让我们可以间接访问到函数内部的变量,延长变量的使用寿命,减少命名空间的污染。
特性:1)函数嵌套函数。 2)函数内部可以引用外部的参数和变量。 3)参数和变量不会被垃圾机制回收
缺点:1)滥用闭包可能导致大量变量不会被垃圾回收,消耗内存,甚至导致卡顿。2)函数内部的没有被释放,占有内存的时间长,容易造成内存泄露。
解决方案:注意编码习惯,在退出函数之前,将不再使用的变量及时的释放。
// 缺点以及优化方案的代码示例
function fn1(){
var arr = new Array[100000]
function fn2(){
console.log(arr.length)
}
return fn2
}
var f = fn1()
f()
// 代码的缺陷之处就在于创建了一个十万个元素的数组,存在了内存中,没有释放,造成了内存浪费。
// 优化代码示例:在使用之后,手动清空变量,释放内存
var f = fn1()
f()
f = null //让内部函数成为垃圾对象,从而回收闭包
// 最简单的闭包函数
function hd(){
let n = 1;
return function(){
console.log( ++n );
}
};
// hd返回一个函数,在外部被接收。hd中的n也就被保存了下来。在执行a函数时候,实现累加的效果
let a = hd();
a(); // 2
a(); // 3
// 下边代码与上边同理,只是多嵌套了一层
function hd(){
let n = 1;
return function(){
let m = 1;
// 多嵌套一层,相同的原理,都是为了把内部变量保存下来,实现累加的效果
return function(){
console.log( ++m );
}
}
};
// hd返回一个函数,在外部被接收。hd中的n也就被保存了下来。在执行a函数时候,实现累加的效果
let a = hd()();
a(); // 2
a(); // 3
// 构造函数中作用域的使用形态
function Hd(){
let n = 1;
this.add = function(){
console.log( ++n );
}
};
let a = new Hd(); // a对象中含有add属性,add又使用到了n。作用域内的值被保留了下来
a.add();
a.add();