🌟前言
哈喽小伙伴们,本文将收录在JavaScript【前端童子功】这个专栏里;这个专栏里边会收录一些JavaScript的基础知识和项目实战;希望大家可以多多支持,你们的支持就是我创作的动力;让我们一起来看看吧🤘
🌟回调函数
js代码会至上而下一条线执行下去,但是有时候我们需要等到一个操作结束之后再进行下一个用户操作,这时候就需要用到回调函数来控制下一个操作的功能。
把一个函数的指针作为另一个函数的参数,当调用这个参数时,被当做参数的这个函数就叫 回调函数 。
function A(callback) { callback(); console.log('主函数'); } function cb(){ console.log("回调函数") } A(cb)
把函数整体作为参数传进去
function A(callback) { callback(); console.log('主函数'); } A(function (){ console.log("回调函数") })
传参给回调函数
function A(callback) { var str = "传递给回调的参数"; callback(str); console.log('主函数'); } A(function (str){ //str 是接收主函数传递的参数 console.log("主函数传递的参数为",str); console.log("回调函数"); })
在执行回调的时候确保它是个函数
function Fn(options, callback) { if (typeof callback === "function") { callback(options); } }
示例:封装each函数简化for循环
function each(n,callback){ for(var i=0;i<n;i++){ callback(i) } } each(10,function(i){ console.log(i) })
🌟递归函数
所谓的 递归函数 就是在函数体内调用本函数。使用递归函数一定要注意,处理不当就会进入死循环。
- 在函数内部直接或间接引用自身。
- 每个递归函数里必定会有终止条件。
求阶乘:
function factorial(c){ if(c == 1){ return c; }else{ return c * factorial(c-1); } } alert(factorial(5));
多维数组遍历:
var new_array=[]; function _getChilds(data){ if(typeof data != "object" || data == null){ new_array.push(data); }else{ getChilds(data); } } function getChilds(data){ for(var i in data){ _getChilds(data[i]); } } var json = { "aa" : {"l" : 1,"m" : 3}, "bb" : 1, "cc" : "abc", "dd" : true, "ee" : null } getChilds(json) console.log(new_array)
🌟闭包
闭包是JavaScript语言的一大特点,主要应用场合为:设计私有的方法和变量。
🌟什么是闭包函数?
理解闭包前,需要先理解 全局作用域和局部作用域 的区别。函数内部可以访问全局作用域下定义的全局变量,而函数外部却无法访问到函数内部定义(局部作用域)的局部变量。
- 概念: 闭包(closure)是定义在一个外部函数内部,并且能够访问外部函数中变量的函数。
- 原理: 作用域链
创建闭包的常见方式,就是在一个函数内部创建另一个函数并返回:
function fun(num){ return function (){ return num; } } var end = fun(100); end(); // 100
上述案例中,num 是fun函数内部的变量,本来只能fun函数内部使用,但是返回的函数也有权限访问num。所以在函数外部通过调用返回的函数即可得到函数内部的变量
function out (){ var n = 1; return function (){ return n++; } } var fn = out(); console.log(fn()) //1 console.log(fn()) //2 console.log(fn()) //3
🌟 闭包三个特性
- 函数嵌套函数
- 函数内部可以引用外部的参数和变量
- 参数和变量不会被垃圾回收机制回收
🌟 闭包的缺点
闭包的缺点就是常驻内存,会增大内存使用量,使用不当很容易造成内存泄漏。 一般函数执行完毕后,局部活动对象就会被销毁,内存中仅仅保存全局作用域。但闭包的情 况不同。
🌟JavaScript的垃圾回收机制
- 在JavaScript中,如果一个对象不再被引用,那么这个对象就会被GC回收。
- 如果两个对象互相引用,而不被第三者所引用,那么这两个互相引用的对象也会被回收。
🌟闭包小案例
那么使用闭包有什么好处呢?
- 希望一个局部变量长期驻扎在内存中
- 避免全局变量的污染
- 私有成员的存在
🌟局部变量长期驻扎内存
function out (){ var n = 1; return function (){ return n++; } } var fn = out(); console.log(fn()) console.log(fn()) console.log(fn())
在循环中直接找到对应的元素的索引
注/ <ul> <li>1111111</li> <li>1111111</li> <li>1111111</li> </ul> var lis=document.getElementsByTagName("li"); for(var i=0;i<lis.length;i++){ (function(i){ lis[i].onclick=function(){ alert(i);//0 1 2 } })(i) } for(var i=0;i<10;i++){ setTimeout(function(){ console.log(i); //10 }, 1000) }
🌟避免全局变量污染
但我们声明一个函数的时候其实就相当于创建一个全局变量 上一个例子中总共占有2个全局变量。
函数表达式 函数自调用:
(function(){ })() var out = (function(){ var n1 = 1; var n2 = 100; return function(){ n1++; n2--; return n1+n2; } })() out() // 1 out() // 2
🌟私有成员的存在
模块化代码
var aa=(function(){ var a=10; function aaa(){ a++; alert(a); } function bbb(){ a+=10; alert(a); } return { a:aaa, b:bbb } })() aa.a(); //11 aa.b(); //21
这样做的好处就是 bbb aaa 这两个函数我们在外面是访问不到的
由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。
🌟[ES6]块级作用域
🌟什么是块级作用域
JS中作用域有:全局作用域、函数作用域。没有块作用域的概念。ECMAScript 6(简称ES6)中新增了块级作用域。
块作用域由 { }
包括,if语句和for语句里面的{ }都属于块作用域。
ES5没有块级作用域概念:
{ var num = 10; } console.log(num) // 10 // for循环结束后,变量i依然会存在 for(var i = 10; i<5;i++){ } console.log(i); // 5
ES6的块级作用域:
{ let num = 10; } console.log(num) // 报错: num is not defined // 由于i是存在于块级作用域中,所以i只能在循环{}中使用 for(let i = 10; i<5;i++){ } console.log(i); // 报错: i is not defined
🌟 函数声明与块级作用域
- ES5中,函数只能在顶层作用域和函数作用域中声明,不能在块级作用域中声明。
- ES6中,在块级作用域内部声明的变量函数,其行为类似于let,即块级作用域外不可使用
function fun(){ console.log("I am outside function.") } if (true) { function fun() { console.log("I am inside function."); } fun(); // I am inside function. } fun(); // I am inside function. 'use strict' //es6 中的运行结果 function fun(){ console.log("I am outside function.") } if (true) { function fun() { console.log("I am inside function."); } fun(); // I am inside function. } fun(); // I am outside function.
🌟函数扩展
🌟[ES6]函数参数的解构赋值
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构
只需要保证实参和形参数据类型同为数组或对象,即可进行函数参数的解构赋值:
function add([x, y]){ return x + y; } add([1, 2]); // 3 function fun({num1:x, num2:y}) { return x + y; } fun({num1: 1, num2: 2}) // 3
🌟[ES6]箭头函数
🌟 基本用法
ES6允许使用“箭头”(=>)定义函数。
var f = v => v;
上面的箭头函数等同于:
var f = function(v) { return v; };
- 如果箭头函数不需要参数,就使用一个
()
或_
代表参数部分。 - 如果箭头函数需要多个参数,就使用一个
()
代表参数部分。
var f = () => 5; var f = _ => 5; // 等同于 var f = function () { return 5 }; var sum = (num1, num2) => num1 + num2; // 等同于 var sum = function(num1, num2) { return num1 + num2; };
如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来。
var sum = (num1, num2) => { console.log(num2); console.log(num1); return num1 + num2; } console.log(sum(1,2)); // 2 1 3
由于大括号被解释为代码块,所以如果箭头函数直接返回一个json,必须在对象外面加上括号。
var person = age => ({ name: "Tom", age: age}); console.log(person(21)); //Object {name: "Tom", age: 21}
箭头函数可以与变量解构结合使用。
var full = ({ name, age}) => name + '-' + age; console.log(full({name:"Tom",age:21})); // Tom-21
箭头函数使得表达更加简洁。
const isEven = n => n % 2 == 0; const square = n => n * n;
箭头函数的其中一个用处是简化回调函数。
// 正常函数写法 var arr1 = [1,2,3].map(function (x) { return x * x; }); console.log(arr1); //[1,4,9] // 箭头函数写法 var arr2 = [1,2,3].map(x => x * x); console.log(arr2); //[1,4,9]
🌟使用注意点
- this对象的指向是可变的,但是在箭头函数中,它是固定的。
function foo() { setTimeout(() => { console.log('id:', this.id); }, 100); setTimeout(function () { console.log("id:",this.id); },100) } var id = 21; foo.call({ id: 42 }); //id: 42 //id: 21
- 不能当做构造函数
- 箭头函数没有arguments对象
🌟写在最后
更多JavaScript知识以及API请大家持续关注,尽请期待。各位小伙伴让我们 let’s be prepared at all times!