js函数、作用域、预编译和作用域链、闭包、立即执行函数

函数声明

①定义函数最基本形式: function () {};与var一样是关键字
②函数名:起名与var类似,多个单词拼接必须符合小驼峰式规则,第一个单词首字母小写,后面的单词首字母大写。
函数可以简化代码,达到高内聚弱偶合原则

函数表达式(忽略函数名)

1.命名函数表达式 var test = function test(){};加分号,表达式会忽略函数名,函数名有name属性eg:
function abc(){} test.name——abc;
2.匿名函数表达式 var demo = function (){};要加分号; 在表达式里会忽略函数名,如果再访问会报错

函数表达式中存储函数的变量就相当于函数,直接用变量名调用:demo();表达式必须加分号。

var f1 = function (){
	console.log('a');
}
f1();——a
var f1 = function (){
	console.log('b');
}
f1();——b
类似于var num=1;
console.log(num);——1
num = 2;
console.log(num);——2

函数组成形式

function关键字 test函数名()参数:形参实参 {}
例:function test(a,b){ }相当于在函数体里var a,var b;test(1,2)对应a,b
参数不限制位数,无论形参有没有把实参表示完,实参都有地方存放;会存放在arguments(实参列表)里。
★只有实参和形参数量相等,实参列表才会和形参发生映射; 实参和形参里面有一个改变另一个也会改变,形参实参数量不相等就不会改变;arguments[]与实参不是同一个;

function sum(a,b){
	a = 2;
	console.log(arguments[0]);
}
sum(1,2);——2

function sum(a,b){
	b = 2;
	console.log(arguments[1]);
}
sum(1);——undefined不发生映射

return 结束条件,返回值
函数没有返回值但是在调用的时候接收了,那么结果就是undefined;如:

function f1(x,y){
	var sum = x + y;
}
var result = f1(10,20);
console.log(result);——undefined
function f2(x,y){
	var sum = x + y;
	return;
}
var result = f2(10,20);
console.log(result);——undefined
函数中没有明确的返回值,调用的时候接收了,结果为undefined
求1-n所有数字的和
function getSum(n){
	var sum = 0;
	for(var i = n; i <= n;i ++){
		sum += i;
	}
	return sum;
}
console.log(getSum(10));
求n-m之间所有数的和
function getSum2(n,m){
	var sum = 0;
	for(var i = 0;i <= m){
		sum += i;
	}
	return sum;
}
求圆的面积
function gets(r){
	return Math.PI * r * r;
}

例子:
function sum(a,b){
	console.log('a');
	return ;
	console.log('b');
}
sum();——a

function sum(){
	return 123;
}
var num = sum();——123
函数的回调(函数作为参数使用)
function test(fn){
	console.log("hi");
	fn();
}
function demo(){
	console.log("hellow");
}
test(demo);

函数作为返回值使用值

function f1(){
	console.log("f1被调用");
	return function (){
		console.log("这是一个函数");
	}
]
var ff = f1();
ff();

作用域

定义:变量(变量作用域又称上下文)和函数生效(能被访问)的区域;
js运行三部曲:

1.语法分析:系统通篇扫描一遍,看是否有语法错误,但不执行;
2.预编译(函数声明整体提升,变量 —声明提升),预编译发生在函数执行的前一刻;
预编译前奏:①imply global暗示全局变量,即任何变量,如果变量未经声明就赋值,此变量就为全局对象(window)所有。

console.log(a);——10不报错
a = 10;

②一切声明的全局变量,全是window属性 。

var a = 123;
相当于window.a = 123;
在window仓库里寻找a
预编译四部曲:

Ⅰ.创建AO对象,Activation Object(执行器上下文),函数执行前产生的存储空间库;
Ⅱ.找形参和变量声明,将变量和形参名作为Ao属性名,值为undefined;
Ⅲ.将实参值和形参相统一;
Ⅳ.在函数体里面找函数声明,值赋予函数体。
3.解释执行(解释一行执行一行)
全局变量如果页面不关闭,那么就不会释放,会占内存,消耗内存;
隐式全局变量:声明变量没用var;
全局作用域:全局变量的使用范围;
局部作用域:局部变量使用的范围;

例子:
function fn(a){
	console.log(a);——function a(){}
	var a = 123;
	console.log(a);——123
	function a(){}
	console.log(a);——123
	var b = function (){}
	console.log(b);——undefined
	function d(){}
}
fn(1);
预编译第一步:形参名变量名作为AO属性名
AO{
	a : undefined,
	b : undefined
}
第二步:形参实参相统一
AO{
	a : 1,
	b : undefined
}
第三步:找函数声明
AO{
	a : function a(){}
	b : undefined,
	d : function d(){}
}
例子:
function test(a,b){
	console.log(a);——1
	c = 0;
	var c;
	a = 3;
	b = 2;
	console.log(b);——function b(){}
	function b(){}
	function d(){}
	console.log(b);——function b(){}
}
test(1);
全局的预编译:

1.生成一个GO对象Global Object;GO === window
2.查找全局变量声明,变量名作为全局对象属性,值为undefined;
3.查找函数声明,值为全局函数引用

例子:
console.log(test);
function test(test){
	console.log(test);——function test(){}
	var test = 234;
	console.log(test);
	function test(){}——234
}
test(1);
var test = 123;
第一步:GO{
	test : undefined
}
AO {
	test : undefined
}
 第二步:GO{
	test : function test(){}
}
AO{
	test : function test(){}
}
案例:1. function bar(){
	return foo;
	foo = 10;
	function foo(){}
	var foo = 11;
}
console.log(bar());——function foo(){}
如果下面有函数,直接输出函数
2.console.log(bar());——11
function bar(){
	foo = 10;
	function foo(){}
	var foo = 11;
	return foo;
}
如果上面被赋值,直接把值打印出来
小例子:
console.log("a");——error
console.log("b");——9
console.log("c");——9
function num(){
	var a = b = c = 9;
	console.log("a");——9
	console.log("b");——9
	console.log("c");——9
}
首先函数声明整体都提升到最上面,在函数内部 var a = b = c =9;声明提升把var a;提升到函数顶端,
a=b=c=9;就相当于,a=9;b=9;c=9;所以在内部访问就都是9。在外部访问时因为var a;经过声明,外部访问
不到内部变量,所以就报错,而b,c 没有声明,相当于是全局的变量,所以访问结果也是9.
fi();——报错error;这是函数表达式不能这样调用;
var f1 = function (){
	console.log(a);
	var a = 10;
}

作用域精解

1.[[score]]域:每个JavaScript都是一个对象,对象中有些属性我们可以访问,但有些不可以,这些属性仅供JavaScript引擎存取,[[score]]就是其中一个。
[[score]]指的就是我们所说的作用域,其中存储了运行期上下下文的集合
2.作用域链:[[score]]中所存储的执行期上下文对象的集合,这个集合呈链式连接;我们把这种链式连接叫做作用域链。(函数嵌套关系)
3.运行期上下文:当函数执行时,会创建一个称为执行器上下文(AO GO)的内部对象。一个执行期上下文定义了一个函数执行时的环境,函数每次执行时对应的执行上下文都是独一无二的,所以多次调用一个函数会导致创建多个执行上下文,当函数执行完毕,它所产生的执行上下文被销毁。
4.查找变量:从作用域链的顶端一次向下查找

案例:function a(){
	function b(){
		var b = 234;
	}
	var a = 123;
	b();
}
var glob = 100;
a();
//a函数被定义 a defined a.[[score]]——产生全局的执行上下文 0 :GO{}
//被执行  a doing  a.[[score]]——新的作用域链   0 : AO{}
//a doing  a.[[score]] ——1 : GO{}
//b defined b.[[score]]——0 : aAo{}所有aAO{}都是同一个
//b doing  b.[[score]]——1 : GO{}
//b doing b[[score]]——0 : bAO{}
// 1 : aAO{}
//2 : GO
存到全局里的文件永远不会被销毁除非全局自身销毁
立即执行函数(函数自调用):此类函数没有声明,在一次执行后立即释放,适合做初始化工作。

格式:
1.w3c建议:(function ( ){ } ())
2.(function ( ){ })( )
只有表达式才能被执行符号执行,执行完此函数就失去意义。
eg: var test = function (){ console.log('a');}()——a
函数声明前加"+","-","!","||",等运算符会和立即执行函数一样
eg:- function(){ }()
趋势上要转换为表达式

eg:+ function test(){
	console.log('a');
}()执行后忽略函数名,销毁引用
eg: var num = (function test(a,b,c){
	var d = a + b + c;
	retuen d;
}(1,2,3)) ——6

案例:(window.foo || (window.foo = 'bar'));window.foo——bar

闭包

定义:当内部函数被保存到外部时会产生闭包,闭包会导致原有作用域链不释放,造成内存泄漏(内存被占用)。简单来说就是两个或多个函数嵌套把里面的函数保存到外部。
作用:1.实现公有变量.

eg:函数累加器
function add() {
	function demo(){
		count++;
		console.log(count);
		}
		return demo;
	}
}
var counter = add();
counter();

2.可以做缓存(存储结构)
3.可以实现封装,属性私有化
4.模块化开发防止污染全局变量

闭包案列:function a(){
	function b(){
		var bbb = 234;
		console.log(aaa;)——123
	}
	var aaa = 123;
	return b;
}
var glob = 100;
var demo = a;
demo();
只要是内部函数被保存到外部就会产生闭包
b保存到a的执行,aAO直到b执行完

闭包案例:打印1-10数字

function test(){
	var arr = [ ];
	for(var i = 0;i < 10;i++){
		//数组每一位成为function作用打印i
		arr[i] = function (){
			document.write(i + "");
		}
	}
	return arr;//返回数组
}
var myArr = test();//接收arr
for(var j = 0;j < 10;j++){
	让数组每一位执行
	myArr[j]();
}
结果:10 10 10 10 10 10 10 10 10 10
for循环转10圈执行了10次产生了十个彼此独立的函数,最后放数组里并且返回,这十个函数与test形成了10对1
的闭包;那么访问test的变量(arr,i)访问的是同一套;这十个函数在外部访问变量时访问的是同一个,是在
test执行完访问的而test执行完后i变成10了

解决后:
function test(){
	var arr = [ ];
	for(var i = 0;i < 10;i++){
	(function (j){
		document.write(j + "");
	}(i));
}
	return arr;//返回数组
}
var myArr = test();//接收arr
for(var j = 0;j < 10;j++){
	让数组每一位执行
	myArr[j]();
}
结果:1,2,3,4,5,6,7,8,9,10

闭包防范:闭包会导致多个执行函数共用一个公有变量;如果不是特殊需要应尽量防止这种情况发生。

你可能感兴趣的:(JavaScript)