argument 预编译 func span 参数 暗示 zhang 全部 所有
函数基本要素:函数声明(function),函数名称,参数(形参,实参),返回值。
1.首先函数命名方式采用小驼峰式写法,即第一个单词小写,后面的单词首字母大写,如 function oneNumber(){}
2.函数表达方式里面有函数表达式,匿名函数表达式
- var a = function lala() {}//函数表达式
- var b = function () {}//匿名函数表达式
为了便于使用以及方便,我们之后基本都是采用匿名函数表达式,并且称为函数表达式。
作用一:返回函数最终执行结果
作用二:终止函数
变量和函数生效的区域叫作用域,作用域分为全局作用域和局部作用域。访问时,里面的作用域可以访问外面的,外面的不能访问里面的。
[[scope]]:每个javascript函数都是一个对象,对象中有些属性我们可以访问,但有些不可以,这些属性仅供javascript引擎存取,[[scope]]就是其中一个。
[[scope]]指的就是我们所说的作用域,其中存储了运行期上下文的集合。
作用域链:[[scope]]中所存储的执行期上下文对象的集合,这个集合呈链式链接,我们把这种链式链接叫做作用域链。
运行期上下文:当函数执行时,会创建一个称为执行期上下文的内部对象。一个执行期上下文定义了一个函数执行时的环境,函数每次执行时产生对应的执行上下
文都是独一无二的,所以多次调用一个函数会导致创建多个执行上下文,当函数执行完毕,执行上下文被销毁。
js运行三步:语法分析,预编译,开始执行
函数里面没有声明直接赋值的变量称为暗示全局变量,能够被全局调用,而且全局变量都是window上的属性,可以通过window.name来调用。
1.创建AO对象(执行上下文对象)
2.找函数声明和函数中的变量声明,并赋值undefined
3.赋值(形参和实参统一)
4.函数体赋值给对应的AO属性
举个栗子看看吧
- function fn(a) {
- console.log(a); // ?unction a(){ }
- var a = 123;
- console.log(a); // 123
- function a() {}
- console.log(a); //123
- console.log(b); //undefined
- var b = function() {};
- console.log(b); //function () {}
- console.log(d); // function d(){}
- function d() {}
- }
- fn(1);
首先产生执行期上下文,然后找函数声明和变量声明a,b,d,下面是整个预编译过程。
a --> undefined --> 1 --> function a ( ) { } --> 123
b --> undefined --> function ( ) { }
d --> undefined --> function d( ) { }
最后得出各AO属性对应的值
function与var之间覆盖的问题,例如:
- function bar(){
- return foo;
- function foo(){ }
- var foo = 111
- }
- console.log(bar()); // 一开始就被return
- function bar(){
- foo = 10;
- function foo(){ {;
- var foo = 11;
- return foo;
- }
- console.log(bar()); // 11 foo被赋值为11,最后才被return!!
以上就是整个预编译过程
说了那么多其实就是为了给闭包做铺垫,闭包会导致多个执行函数共用一个公有变量。所以一般如果不是特殊需要,尽量少用。容易污染全局变量。
举个栗子
- function a(){
- function b(){
- var bbb = 234;
- console.log(aaa);
- }
- var aaa = 123;
- return b;
- }
- a(); //*****************************function b() {}
- var demo = a();
- demo();
最后我们会发现,demo --> function b () { },demo() --> 123。这是因为当b执行完了相当于a也执行完了,这样a会把执行上下文销毁,但是b已经被return出去并且给了demo,当demo执行时也就是相当于function b () { }执行,得出123。这就是内存泄露,原有的作用域链不释放导致的。
来个栗子巩固一下:
- function a() {
- var aaa = 100;
- function b() {
- console.log(aaa);
- }
- return b;
- }
- var demo = a();
- demo(); //100 b的劳动成果已经保存到了demo里面
当a函数定义的时候,产生一个执行上下文,里面有一个aaa,和一个函数b,当b定义的时候,已经含有a的劳动成果,意思就是它已经有a的执行上下文,并且在b执行的时候,产生它自己的执行上下文,最后当a函数执行完之后,把函数b返回到了全局作用域,虽然a执行完,并且销毁了它自己的执行上下文,但是因为其内部b函数的存在,仍然有a的全部执行上下文,所以,仍然可以通过demo来访问function a里面的aaa变量。
- function add() {
- var num = 0;
- function demo() {
- num++;
- console.log(num);
- }
- return demo;
- }
- var test = add();
- test(); //1
- test(); //2
对于上述栗子,公有变量就是num。add函数将demo函数返回出去,demo函数依然有add函数的执行上下文,每次执行test(),就相当于执行demo函数,每次访问的num都是同一个
num变量,这样num就是一个公有变量了,通过这种方式就能利用闭包产生一个累加器了。
- function test() {
- var num = 0;
- function a() {
- console.log(++num);
- }
- function b() {
- console.log(--num);
- }
- return [a, b];
- }
- var arr = test();
- arr[0](); //1
- arr[1](); //0
a函数和b函数都被return到了外部,这样a函数和b函数都与num产生了一个闭包,并且a和b执行的都是同一个变量,当a改变num的时候,b的num也会发生改变,同理,b操作了
num,a的num也会发生改变,因为它们指向的num是同一个num,这就相当于一个缓存。
再来一个栗子
- function eater() {
- var food = "";
- var obj = {
- eat: function() {
- if (food == "") {
- console.log(‘empty‘);
- } else {
- console.log("I am eating " + food);
- food = "";
- }
- },
- push: function(myFood) {
- food = muFood
- }
- }
- return obj;
- }
- var eater1 = eater();
- eater1.eat();
- eater1.push(‘orange‘);
- eater1.eat();
和上一个栗子一样,obj对象被return到了外部,并且用eater1来接收。eater1.eat()和eater1.push()所对应的food是同一个,这就是利用闭包产生缓存机构。
这个作用的了解需要先了解一下构造函数
- function Deng() {
- var prepareWife = "xiaozhang";
- var obj = {
- name: "Laodeng",
- age: 40,
- sex: "male",
- wife: "xiaoliu",
- divorce: function() {
- this.wife = delete this.wife;
- },
- getMarried: function() {
- this.wife = prepareWife;
- },
- changePrepare: function(someone) {
- preparewife = someone;
- },
- sayMywife: function() {
- console.log(this.wife);
- }
- }
- return obj;
- }
- deng = Deng();
运行一下看看
- deng.sayMyWife() //"xiaoliu"
- deng.divorce() //undefined (没有返回值)
- deng.sayMywife() // true已经删除
- deng.changePrepare(‘xiaoxiaozhang‘) //undefined (函数没有返回值)********
- deng.getMarried() //undefined
- deng.sayMywife() //"xiaoxiaozhang"
- deng.prepareWife //undefined
prepareWife函数里面的变量,不在全局作用域里,所以我们访问不了,但是我们所有的操作都是围绕prepareWife来进行的,它们都可以正常访问这个变量,所以,像这种只能通过与
这个变量产生闭包的方法,属性,才能给对那个变量进行访问,所以,我们就称之为,私有化变量,我们可以通过定制接口(各种方法),来对变量的安全程度进行设置。
此类函数没有声明,在一次执行过后即释放。适合做初始化工作。比如有些数学公式,或者是其他一些常数的计算,我们没有必要把它一直放在全局的空间里,这样会很好内存,于
是就诞生了立即执行函数。
- var x = (function(a, b){
- return(a + b) ;
- }(1, 2)) // x = 3
立即执行函数特点就是当JavaScript引擎解析到这个语句的时候就会马上执行,执行结束后马上把自己的执行上下文都销毁。这样就可以释放这里的内存,立即执行函数可以有返回值
以及形参等。
我们要知道函数声明不能执行,只有函数才能执行,所以
函数声明----->表达式
function test() { } // 函数声明,不是表达式
var test = function () ; // 函数表达式
我们可以把函数声明转换成表达式
1.+function test(){ } -----> +号运算符,这样就将函数声明转换成表达式,就可以执行了
2. !function test(){ } ------> !
3. (function test(){ })( ) -------> ( )
想要实现打印0-9,结果却出人意料。。。。。。。(出现了10个10)。这个我想了好久才想通了,唉,脑子不够用。。。
- function test() {
- var arr = [];
- for (var i = 0; i < 10; i++) {
- arr[i] = function() {
- console.log(i + ",");
- }
- }
- return arr;
- }
- var demo = test();
- for (var j = 0; j < 10; j++) {
- demo[j]();
- } // 10 * 10
这是为什么呢?输出的10个全是10,说明这里的i都是同一个i,为什么会这样呢,原来是function执行的时候i已经就是10了,由于test与arr之间产生了闭包,先这样说吧,每次return出
去一个arr,但是function现在还没有执行,也就是都是arr[i],当终止循环的时候i=10,这时候function执行的时候10个i都是相加到10的那个i,是同一个i,所以最后打印出10个10。理解了
吗?感觉说的太通俗了,希望大家能够理解哈。
如果觉得我说的有问题,欢迎提出来,大家一起进步哈!
- function test() {
- var arr = [];
- for(var i = 0; i < 10; i++){
- (function (j) {
- console.log(j);
- }(i))
- }
- return arr;
- }
利用立即执行函数,每次访问的i都不一样,所以打印出来的就是0-9了。
最后再来一个栗子
- a = 100;
- function demo(e) {
- function e() {}
- arguments[0] = 2;
- document.write(e); //2 因为形参列表将e改变为2
- if (a) {
- var b = 123;
- function c() {}
- }
- var c;
- a = 10;
- var a;
- document.write(b); // undefined
- f = 123;
- docuemnt.write(c); //function (){}
- docuemnt.write(a); // 10
- }
- var a;
- demo(1) docuemnt.write(a);
- document.write(f);
JavaScript初阶(三)--------函数、闭包、立即执行函数
argument 预编译 func span 参数 暗示 zhang 全部 所有
原文:http://www.cnblogs.com/sunshinehu/p/7922582.html
来源: http://www.bubuko.com/infodetail-2412844.html