1. 函数
-
函数是一个可重用的代码块,用来完成某个特定功能。每当需要反复执行一段代码时,可以利用函数来避免重复书写相同代码。
-
函数包含着的代码只能在函数被调用时才会执行,就可以避免页面载入时执行该脚本
-
在JavaScript中,可以使用以下三种方法来定义一个函数
-
使用function语句定义函数
-
使用Function()构造函数来定义函数
-
在表达式中定义函数
-
1.1 函数的概念
-
在JavaScript中,可能会编写非常多的相同的或者功能相似的代码,这些代码可以多次重复使用。
-
函数:就是封装了一段可以被重复调用执行的代码块,通过此代码块可以实现大量的代码的重复使用。
-
函数(又称为方法)用于对一大段为了达到某种目的的代码进行归类,以使代码更具有条理。
1.2 函数的使用
1.2.1 声明函数
语法:
function 函数名(){ // 函数体 }
1.2.2 调用函数
1.直接调用
myFunction();或window.myFunction()
2.事件处理中调用
<div onclick="myFunction()"></div>
3.将函数的返回值赋给变量
var t = myFunction();
1.2.3 函数的封装
把一个或者多个功能通过函数的方式封装起来,对外只提供一个简单的函数接口。
1.3 函数的参数
-
形参:在声明函数的小括号里面是形参(形式上的参数)
-
实参:在调用函数的小括号里面是实参(实际上的参数)
-
形参与实参个数不匹配
-
实参多于形参,只取到形参的个数,可以在函数内部通过arguments关键字取到所有实参
function foo() { console.log(arguments.length); // 输出传递的参数个数 console.log(arguments[0]); // 输出第一个参数的值 console.log(arguments[1]); // 输出第二个参数的值 // ... } foo(10, 'hello');
-
实参少于形参,多的形参定义为undefined
-
1.4 函数的返回值
-
return语句
语法:
function 函数名(){ return 需要返回的结果; }
-
return语句之后的内容不再执行。
-
return只返回一个值,如果用逗号隔开多个值,只返回最后一个。
-
如果函数有return,则返回return后面的值,如果函数没有return,则函数返回的结果undefined。
1.5 函数的两种声明方式
-
命名函数(函数声明)
function 函数名(){}
-
函数表达式
var 变量名 = function(){}
2 作用域
定义:
作用域就是变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期。
在JavaScript中,变量的作用域有全局作用域和局部作用域两种。
2.1 全局作用域(Global Scope)
在代码中任何地方都能访问到的对象拥有全局作用域,一般来说以下几种情形拥有全局作用域 :
1.最外层函数和在最外层函数外定义的变量拥有全局作用域
var authorName = '山边小溪'; function doSomething() { var blogName = '梦想天空'; function innerSay() { console.log(blogName); } innerSay(); } console.log(authorName); // 山边小溪 console.log(blogName); // 脚本错误 doSomething(); // 梦想天空 innerSay(); // 脚本错误
2.所有未定义直接赋值(没有var关键字)的变量自动声明为拥有全局作用域
function doSomething() { var authorName = '山边小溪'; blogName = '梦想天空'; console.log(authorName); } doSomething(); // 山边小溪 console.log(blogName); // 梦想天空 console.log(authorName); // 脚本错误
3.所有window对象的属性拥有全局作用域
一般情况下,window对象的内置属性都拥有全局作用域,例如window.name、window.location等
2.2 局部作用域(两种)(Local Scope)
函数作用域
和全局作用域相反,局部作用域一般只在固定的代码片段内可访问到,最常见的的就是在函数内部,所有也会看到有人把这种作用域称为函数作用域。
函数体内的变量声明(var声明的变量)会提前到函数体顶部,同时变量初始化还在原来的位置
// blogName和函数innerSay都只拥有局部作用域 function doSomething() { var blogName = '梦想天空'; function innerSay() { alert(blogName); } innerSayName(); } console.log(blogName); // 脚本错误 innerSay(); // 脚本错误
ES6中的块级作用域
ES6
中新增了块级作用域。块作用域由 { }
包括,if
语句和 for
语句里面的 { }
也属于块作用域。
块级作用域由let声明变量,const声明常量
2.3 什么是作用域链
作用域链(Scope Chain)是指在嵌套的作用域中查找变量时依次检查的顺序。当需要访问一个变量时,JavaScript会从当前作用域开始搜索,直到找到该变量或搜索到达全局作用域。如果变量在作用域链中的某个地方找到,则可以访问该变量,否则将抛出引用错误。
3.自执行函数
自执行函数(Immediately Invoked Function Expression,IIFE)是一种在定义后立即执行的 JavaScript 函数。它的语法形式是将函数定义包裹在一对括号中,然后在后面加上一对空括号,如下所示:
(function() { // 函数体 })();
这样定义的函数会立即执行,并且不会在全局作用域中创建额外的变量。自执行函数常用于创建私有作用域、模块化开发和避免变量污染等场景。
自执行函数的效果
自执行函数可以用来保存变量的作用域,防止污染全局变量 以一个经典的面试题为例;
<ul> <li>这是第1个li</li> <li>这是第2个li</li> <li>这是第3个li</li> <li>这是第4个li</li> <li>这是第5个li</li> <li>这是第6个li</li> </ul>
一个列表里有6个li,要求点击li的时候打印当前被点击li的索引。 如果我们直接通过for循环绑定事件:
var lis = document.querySelectorAll('li'); for(var i = 0;i<lis.length;i++){ lis[i].onclick = function (){ console.log(i) } } // 无论哪个li被点击,打印的永远都是相同的值,因为在所有的点击事件中,访问的都是全局的 i,当事件触发时,i的值已经变成是lis.length了。
在es6语法出现之后,我们使用 let 关键字创建的块级作用域可以解决这种问题,在 let 出现以前,通常是使用匿名函数+闭包的方式创建函数作用域来保存每一步循环里 i 的值;
var lis = document.querySelectorAll('li'); for(var i = 0;i<lis.length;i++){ (function(i){ lis[i].onclick = function (){ console.log(i) } })(i) }
// 使用这种方法,每次循环都会创建一个匿名函数,这个匿名函数生成了闭包的效果,新建了一个作用域,
// 这个作用域接收到每次循环的i值保存了下来,即使循环结束,闭包形成的作用域也不会被销毁;
// 事件处理函数中访问的 i 不再是全局变量,而且匿名函数中的局部变量。
闭包
什么是闭包:闭包就是能够读取其他函数内部变量的函数。例如在javascript中,只有函数内部的子函数才能读取局部变量,所以闭包可以理解成“定义在一个函数内部的函数“。在本质上,闭包是将函数内部和函数外部连接起来的桥梁。 特点:
-
闭包就是一个特殊的函数
-
函数内部可以引用函数外部的参数和变量【能访问到其他作用域中的变量】
-
参数和变量不会被垃圾回收机制回收【延长变量的生命周期】
创建闭包
// 简单的闭包, var num = 10; function show(){ console.log(num); } // 局部变量在全局使用 function show(){ var num = 10; return function(){ // 闭包函数 return num }; } var b = show(); console.log(b()); // 局部变量 function f1(){ var n=999; } // 局部变量在全局使用 function f1(){ var n=999; function f2(){ alert(n); // 999 } return f2; // 闭包函数 } f1()()
// 总结:闭包就是能够读取其他函数内部变量的函数。
案例:将普通函数改为闭包函数
// 普通函数 function show(){ var m = 1; function inner(){ var n = 0; console.log(++n); console.log(++m); }; inner(); } // 闭包函数 function show(){ var m = 1; return function(){ var n = 0; console.log(++n); console.log(++m); }; }
闭包的用途:
-
是前面提到的可以读取函数内部的变量
-
就是让这些变量的值始终保持在内存中。
闭包的缺点
比普通函数更占用内存,会导致网页性能变差,在IE下容易造成内存泄露。
什么是内存泄漏
首先,需要了解浏览器自身的内存回收机制。 每个浏览器会有自己的一套回收机制,当分配出去的内存不使用的时候便会回收;内存泄露的根本原因就是你的代码中分配了一些‘顽固的’内存,浏览器无法进行回收,如果这些’顽固的’内存还在一直不停地分配就会导致后面所用内存不足,造成泄露。
闭包造成内存泄漏
因为闭包就是能够访问外部函数变量的一个函数,而函数是必须保存在内存中的对象,所以位于函数执行上下文中的所有变量也需要保存在内存中,这样就不会被回收,如果一旦循环引用或创建闭包,就会占据大量内存,可能会引起内存泄漏 闭包参考文档:https://www.cnblogs.com/huanxiongs02/p/14698192.html
函数案例
//冒泡排序 var arr = [3,8,6,1,9,5] function sort(arr){ var temp; for(var i=0; i<arr.length-1; i++){ for(var j=0; j<arr.length-1-i; j++){ if(arr[j] > arr[j+1]){ temp = arr[j+1] arr[j+1] = arr[j] arr[j] = temp } } } return arr } console.log(sort(arr));
// 获取数组中的最大值 function getMax(arr){ var newArr = arr.sort(function(a,b){ return b - a; }) return newArr[0]; var max = arr[0]; for(var i=1;i<arr.length;i++){ if(arr[i]>max){ max = arr[i]; } } return max; } console.log(getMax([6,3,8,23,45,1,99]));
//反转数组
// 方法一 function reverse(arr){ var newArr = []; for(var i=0;i<arr.length;i++){ newArr[arr.length-i-1] = arr[i]; } return newArr; } var arr1 = reverse([1,4,7,9,12,16]); console.log(arr1); // 方法二 function reverse(arr){ var newArr = []; for(var i=arr.length-1;i>=0;i--){ newArr[newArr.length] = arr[i] } return newArr; } var arr1 = reverse([1,4,7,9,12,16]); console.log(arr1);
标签:function,闭包,console,函数,作用域,var From: https://www.cnblogs.com/c-pp/p/JavaScript_4.html