亿藤互联网站建设开发,WordPress底部自定义插件,asp.net 4.0网站开发,深圳建网站兴田德润团队前言
程序中的foo、bar、baz
在学习编程的过程中#xff0c;你可能会经常看到foo、bar、baz这些名词#xff1a;
它们通常被用来作为函数、变量、文件的名词#xff1b;目前已经编程了计算机编程的术语一部分#xff1b;但是它们本身并没有特别的用途和意义#xff1b;…前言
程序中的foo、bar、baz
在学习编程的过程中你可能会经常看到foo、bar、baz这些名词
它们通常被用来作为函数、变量、文件的名词目前已经编程了计算机编程的术语一部分但是它们本身并没有特别的用途和意义常被称之为 伪变量metasyntactic variable
那么它们有什么由来吗
事实上foo、bar这些名词最早从什么时候、地方流行起来的一直是由争论的一种说法是通过Digital迪吉多数字设备公司成立于1957年的美国电脑公司的手册说明流行起来的一种说法是说源自于电子学中的反转foo信号也有一种说法是foo因为出现在了一个漫画中漫画中foo代表“好运”与中文的福读音类似
总之foo、bar、baz已经是编程领域非常常用的名词。
我个人也比较习惯在写一些变量、函数名词时使用这些词汇大家做一个了解
一、认识函数
1.函数的定义 函数其实就是某段代码的封装这段代码帮助我们完成某一个功能 默认情况下JavaScript引擎或者浏览器会给我们提供一些已经实现好的函数 我们也可以编写属于自己的函数
在开发程序时使用函数可以提高编写的效率以及代码的重用
2.函数的声明
JavaScript 有三种声明函数的方法。
2.1 function 命令
function命令声明的代码区块就是一个函数。
function print(s) {console.log(s);
}上面的代码命名了一个print函数以后使用print()这种形式就可以调用相应的代码。这叫做函数的声明Function Declaration
2.2 函数表达式
除了用function命令声明函数还可以采用变量赋值的写法。
var print function (s) {console.log(s);
};这种写法将一个匿名函数赋值给变量。这时这个匿名函数又称函数表达式Function Expression因为赋值语句的等号右侧只能放表达式。
采用函数表达式声明函数时function命令后面不带有函数名。如果加上函数名该函数名只在函数体内部有效在函数体外部无效。
var print function x(){console.log(typeof x);
};x
// ReferenceError: x is not definedprint()
// function这种写法的用处有两个一是可以在函数体内部调用自身二是方便除错除错工具显示函数调用栈时将显示函数名而不再显示这里是一个匿名函数。因此下面的形式声明函数也非常常见。
var f function f() {};2.3 Function 构造函数
第三种声明函数的方式是Function构造函数。
var add new Function(x,y,return x y
);// 等同于
function add(x, y) {return x y;
}你可以传递任意数量的参数给Function构造函数只有最后一个参数会被当做函数体如果只有一个参数该参数就是函数体。
var foo new Function(return hello world;
);// 等同于
function foo() {return hello world;
}Function构造函数可以不使用new命令返回结果完全一样。
总的来说这种声明函数的方式非常不直观几乎无人使用。
3.函数的重复声明
如果同一个函数被多次声明后面的声明就会覆盖前面的声明。
function f() {console.log(1);
}
f() // 2function f() {console.log(2);
}
f() // 24.函数的调用
调用函数时要使用圆括号运算符。圆括号之中可以加入函数的参数。
function add(x, y) {return x y;
}add(1, 1) // 2上面代码中函数名后面紧跟一对圆括号就会调用这个函数。
5.return语句
函数体内部的return语句表示返回。JavaScript 引擎遇到return语句就直接返回return后面的那个表达式的值后面即使还有语句也不会得到执行。也就是说return语句所带的那个表达式就是函数的返回值。return语句不是必需的如果没有的话该函数就不返回任何值或者说返回undefined。
function foo() {console.log(123);
}
var result1 foo();
console.log(result1);//undefined6.递归
函数可以调用自身这就是递归recursion。下面就是通过递归计算斐波那契数列的代码。
function fib(num) {if (num 0) return 0;if (num 1) return 1;return fib(num - 2) fib(num - 1);
}fib(6) // 8二、函数的特性
1.头等公民
JavaScript 语言将函数看作一种值与其它值数值、字符串、布尔值等等地位相同。
凡是可以使用值的地方就能使用函数。比如可以把函数赋值给变量和对象的属性也可以当作参数传入其他函数或者作为函数的结果返回。函数只是一个可以执行的值此外并无特殊之处。
由于函数与其他数据类型地位平等所以在 JavaScript 语言中又称函数为第一等公民。 通常我们对将函数作为头等公民的编程方式称之为函数式编程 function add(x, y) {return x y;
}// 将函数赋值给一个变量
var operator add;// 将函数作为参数和返回值
function a(op){return op;
}
a(add)(1, 1)
// 22.函数名的提升
JavaScript 引擎将函数名视同变量名所以采用function命令声明函数时整个函数会像变量声明一样被提升到代码头部。所以下面的代码不会报错。
f();function f() {}表面上上面代码好像在声明之前就调用了函数f。但是实际上由于“变量提升”函数f被提升到了代码头部也就是在调用之前已经声明了。
不过这种特性只针对function命令的声明方式有效函数表达式没有这个特性
foo();
// TypeError: undefined is not a functionvar foo function foo() {}上面的代码等同于下面的形式
var f;
f();
f function () {};上面代码第二行调用f的时候f只是被声明了还没有被赋值等于undefined所以会报错。
注意如果像下面例子那样采用function命令和var赋值语句声明同一个函数由于存在函数提升最后会采用var赋值语句的定义。
var f function () {console.log(1);
}function f() {console.log(2);
}f() // 1三、函数的属性和方法
1.name属性
函数的name属性返回函数的名字。
function f1() {}
f1.name // f1如果是通过变量赋值定义的函数那么name属性返回变量名。
var f2 function () {};
f2.name // f2如果变量的值是一个具名函数那么name属性返回function关键字之后的那个函数名。
var f3 function myName() {};
f3.name // myNamename属性始终显示函数第一个赋值的变量名
var f1 function f2() {}function printFnName(fn) {console.log(fn.name);
}
printFnName();//f22.length 属性
函数的length属性返回函数预期传入的参数个数即函数定义之中的参数个数。
function f(a, b) {}
f.length // 2上面代码定义了空函数f它的length属性就是定义时的参数个数。不管调用时输入了多少个参数length属性始终等于2。
length属性提供了一种机制判断定义时和调用时参数的差异以便实现面向对象编程的“方法重载”overload。
3.toString()
函数的toString()方法返回一个字符串内容是函数的源码。
function f() {a();b();c();
}f.toString()
// function f() {
// a();
// b();
// c();
// }对于那些原生的函数toString()方法返回function (){[native code]}。
Math.sqrt.toString()
// function sqrt() { [native code] }函数内部的注释也可以返回。
function f() {/*这是一个多行注释
*/}f.toString()
// function f(){/*
// 这是一个
// 多行注释
// */}利用这一点可以变相实现多行字符串。
var multiline function (fn) {var arr fn.toString().split(\n);return arr.slice(1, arr.length - 1).join(\n);
};function f() {/*这是一个多行注释
*/}multiline(f);
// 这是一个
// 多行注释四、函数的参数
1.概述
函数运行的时候有时需要提供外部数据不同的外部数据会得到不同的结果这种外部数据就叫参数。
function foo(x, y) {return x y;
}
foo(1, 2);形参参数 parameter定义 函数时小括号中的参数是用来接收参数用的在函数内部 作为变量使用实参参数 argument调用 函数时小括号中的参数是用来把数据传递到 函数内部 用的
2.参数的省略
函数参数不是必需的JavaScript 允许省略参数。
function f(a, b) {return a;
}f(1, 2, 3) // 1
f(1) // 1
f() // undefinedf.length // 2需要注意的是函数的length属性与实际传入的参数个数无关只反映函数预期传入的参数个数。
但是没有办法只省略靠前的参数而保留靠后的参数。如果一定要省略靠前的参数只有显式传入undefined。
function f(a, b) {return a;
}f( , 1) // SyntaxError: Unexpected token ,(…)
f(undefined, 1) // undefined3.传递方式
函数参数如果是原始类型的值数值、字符串、布尔值传递方式是值传递passes by value。
var p 2;function f(p) {p 3;
}
f(p);p // 2如果函数参数是复合类型的值数组、对象、其他函数传递方式是引用传递pass by reference。
var obj { p: 1 };function f(o) {o.p 2;
}
f(obj);obj.p // 24.arguments 对象
4.1 定义
由于 JavaScript 允许函数有不定数目的参数所以需要一种机制可以在函数体内部读取所有参数。这就是arguments对象的由来。 在ES6的箭头函数中已经将这个参数移除。用新增的可变参数写法替代了arguments arguments对象包含了函数运行时的所有参数arguments[0]就是第一个参数arguments[1]就是第二个参数以此类推。这个对象只有在函数体内部才可以使用。
var f function (one) {console.log(arguments[0]);console.log(arguments[1]);console.log(arguments[2]);
}f(1, 2, 3)
// 1
// 2
// 3正常模式下arguments对象可以在运行时修改。
var f function(a, b) {arguments[0] 3;arguments[1] 2;return a b;
}f(1, 1) // 5严格模式下arguments对象与函数参数不具有联动关系不会影响到实际的函数参数。
var f function(a, b) {use strict; // 开启严格模式arguments[0] 3;arguments[1] 2;return a b;
}f(1, 1) // 2通过arguments对象的length属性可以判断函数调用时到底带几个参数。
function f() {return arguments.length;
}f(1, 2, 3) // 3
f(1) // 1
f() // 04.2 与数组的关系
需要注意的是虽然arguments很像数组但它是一个对象。数组专有的方法比如slice和forEach不能在arguments对象上直接使用。
如果要让arguments对象使用数组方法真正的解决方法是将arguments转为真正的数组。下面是三种常用的转换方法slice方法和逐一填入新数组。
// 方式一
var args Array.prototype.slice.call(arguments);// 方式二
var args [];
for (var i 0; i arguments.length; i) {args.push(arguments[i]);
}// 方式三
var args Array.from(arguments);4.3 callee 属性
arguments对象带有一个callee属性返回它所对应的原函数。
var f function () {console.log(arguments.callee f);
}f() // true可以通过arguments.callee达到调用函数自身的目的。这个属性在严格模式里面是禁用的因此不建议使用。
5.同名参数
如果有同名的参数则取最后出现的那个值。
function f(a, a) {console.log(a);
}f(1, 2) // 2调用函数f()的时候没有提供第二个参数a的取值就变成了undefined。这时如果要获得第一个a的值可以使用arguments对象。
function f(a, a) {console.log(arguments[0]);//1console.log(a);//undefined
}f(1) // 1五、函数作用域
1.定义
作用域scope指的是变量存在的范围。在 ES5 的规范中JavaScript 只有两种作用域一种是全局作用域变量在整个程序中一直存在所有地方都可以读取另一种是函数作用域变量只在函数内部存在。
对于顶层函数来说函数外部声明的变量就是全局变量global variable它可以在函数内部读取。
var v 1;function f() {console.log(v);
}f()
// 1在函数内部定义的变量外部无法读取称为“局部变量”local variable。
function f(){var v 1;
}v // ReferenceError: v is not defined函数内部定义的变量会在该作用域内覆盖同名全局变量。
var v 1;function f(){var v 2;console.log(v);
}f() // 2
v // 1对于var命令来说局部变量只能在函数内部声明在其他区块中声明一律都是全局变量。 var x 1;
if (x 1) {var a 10;
}
console.log(a);//undefined2.函数变量的提升
与全局作用域一样函数作用域内部也会产生“变量提升”现象。var命令声明的变量不管在什么位置变量声明都会被提升到函数体的头部。
function foo(x) {if (x 100) {var tmp x - 100;}
}// 等同于
function foo(x) {var tmp;if (x 100) {tmp x - 100;};
}3.函数本身的作用域
函数本身也是一个值也有自己的作用域。它的作用域与变量一样就是其声明时所在的作用域与其运行时所在的作用域无关。
var a 1;
var x function () {console.log(a);
};function f() {var a 2;x();
}f() // 1函数执行时所在的作用域是定义时的作用域而不是调用时所在的作用域。
函数体内部声明的函数作用域绑定函数体内部。
function foo() {var x 1;function bar() {console.log(x);}return bar;
}var x 2;
var f foo();
f() // 1上面代码中函数foo内部声明了一个函数barbar的作用域绑定foo。当我们在foo外部取出bar执行时变量x指向的是foo内部的x而不是foo外部的x。正是这种机制构成了下文要讲解的“闭包”现象。
六、函数的其他知识点
1.立即调用的函数表达式IIFE
根据 JavaScript 的语法圆括号()跟在函数名之后表示调用该函数。比如print()就表示调用print函数。
有时我们需要在定义函数之后立即调用该函数。这时你不能在函数的定义之后加上圆括号这会产生语法错误。
function(){ /* code */ }();
// SyntaxError: Unexpected token (产生这个错误的原因是function这个关键字既可以当作语句也可以当作表达式。
// 语句
function f() {}// 表达式
var f function f() {}当作表达式时函数可以定义后直接加圆括号调用。
var f function f(){ return 1}();
f // 1为了避免解析的歧义JavaScript 规定如果function关键字出现在行首一律解释成语句。因此引擎看到行首是function关键字之后认为这一段都是函数的定义不应该以圆括号结尾所以就报错了。
函数定义后立即调用的解决方法就是不要让function出现在行首让引擎将其理解成一个表达式。最简单的处理就是将其放在一个圆括号里面。
(function(){ /* code */ }());
// 或者
(function(){ /* code */ })();上面两种写法都是以圆括号开头引擎就会认为后面跟的是一个表达式而不是函数定义语句所以就避免了错误。
举一反三任何让解释器以表达式来处理函数定义的方法都能产生同样的效果比如下面三种写法。
var i function(){ return 10; }();
true function(){ /* code */ }();
0, function(){ /* code */ }();甚至可以这样写
!function () { /* code */ }();
~function () { /* code */ }();
-function () { /* code */ }();
function () { /* code */ }();通常情况下只对匿名函数使用这种“立即执行的函数表达式”。 作用
不必为函数命名避免了污染全局变量IIFE 内部形成了一个单独的作用域可以封装一些外部无法读取的私有变量。
// 写法一
var tmp newData;
processData(tmp);
storeData(tmp);// 写法二
(function () {var tmp newData;processData(tmp);storeData(tmp);
}());上面代码中写法二比写法一更好因为完全避免了污染全局变量。
2.闭包
闭包closure是 JavaScript 语言的一个难点也是它的特色很多高级应用都要依靠闭包实现。
这里将闭包放到 javascript高级 进行讲解详情请看