下面小编就为大家带来一篇 JavaScript 基础函数_深入剖析变量和作用域。小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
Javascript 是一种由 Netscape 的 LiveScript 发展而来的原型化继承的基于对象的动态类型的区分大小写的客户端脚本语言,主要目的是为了解决服务器端语言,比如 Perl,遗留的速度问题,为客户提供更流畅的浏览效果。
函数定义和调用
定义函数,在 JavaScript 中,定义函数的方式如下:
- function abs(x){
- if(x >=0){
- return x;
- }else{
- return -x;
- }
- }
上述 abs() 函数的定义如下:
function 指出这是一个函数定义;
abs 是函数的名称;
(x) 括号内列出函数的参数,多个参数以,分隔;
{...} 之间的代码是函数体,可以包含若干语句,甚至可以没有任何语句。
注意:函数体内部的语句在执行时,一旦执行到 return 时,函数就执行完毕,并将结果返回。因此内部通过条件判断和循环可以在实现非常复杂的。
如果没有 return 语句,函数执行完毕后也会返回结果,只是结果为 undefined。
由于 JavaScript 的函数也是一个对象,上述定义的 abs() 函数实际上是一个函数对象,而函数名 abs 可以视为指向该函数的变量。
- var abs = function(x){
- if(x >= 0){
- return x;
- } else {
- return -x;
- }
- }
在这种方式下,function (x) {...} 是一个匿名函数,它没有函数名。但是,这个匿名函数赋值给了变量 abs,所以,通过变量 abs 就可以调用该函数。
两种定义完全等价,注意第二种方式按照完整语法需要在函数体末尾加一个;,表示赋值语句结束。
调用函数时,按顺序传入参数即可:
abs(10); // 返回 10
abs(-9); // 返回 9
由于 JavaScript 允许传入任意个参数而不受影响调用,因此传入的参数比定义的参数多也没有问题,虽然函数内部并不需要这些参数。
abs(10,'blablabla'); // 返回 10
abs(-9,'haha','hehe',null) // 返回 9
传入的参数比定义的少也没有问题
abs(); 返回 NaN
此时 abs(x) 函数的参数 x 将收到 undefined, 计算结果为 NaN
- function abs(x){
- if(typeof x !=='number'){
- throw 'Not a number':
- }
- if(x >=0){
- return x;
- }else{
- return -x;
- }
- }
arguments
JavaScript 还有一个免费赠送的关键字 arguments, 它只在函数内部起作用,并且永远指向当前函数的调用者传入的所有参数。
- function foo(x){
- alert(x); // 10
- for(var i=0; i < arguments.length;++){
- alert(arguments[i]); // 10,20,30
- }
- }
- foo(10.20,30)
利用 arguments,你可以获得调用者传入的所有参数。也就是说,即使函数不定义任何参数,还是可以拿到参数的值:
- function abs(){
- if(arguments.length ===0){
- return 0;
- }
- var x = arguments[0]
- return x >=0 ? x : -x;
- }
- abs(); //0
- abs(10); // 10
- abs(-9) //9
实际上 arguments 最常用于判断传入参数的个数。你可能会看到这样的写法:
// foo(a[,b],c)
// 接受 2~3 个参数,b 是可选参数,如果只要出入两个参数,b 默认为 null
- function foo(a,b,c){
- if(arguments.length ===2){
- // 实际拿到的参数是a 和b c 为undefined
- c = b;
- b = null; // b 变为默认值
要把中间的参数 b 变为 "可选" 参数,就只能通过 arguments 判断,然后重新调整参数并赋值。
rest 参数
由于 JavaScript 函数允许接收任意个参数,遇事我们就不得不用 arguments 来获取所有的参数:
- function foo(a,b){
- var i, rest = [];
- if(arguments.length > 2){
- for(i = 2; i < arguments.length; i++){
- rest.push(arguments[i]);
- }
- }
- console.log('a =' + a);
- console.log('b = ' + b);
- console.log(rest);
- }
为了获取除了已定义参数 a、b 之外的参数,我们不得不用 arguments,并且循环要从索引 2 开始以便排除前两个参数,这种写法很别扭,只是为了获得额外的 rest 参数,有没 有更好的方法?
ES6 标准引入了 rest 参数,上面的函数可以改写为:
- function foo(a,b,...rest){
- console.log('a = ' + a);
- console.log('b = ' + b);
- console.log(rest);
- }
- foo(1,2,3,4,5);
- //结果
- // a = 1
- // b = 2
- // Array[3,4,5]
- foo(1)
- // 结果
- // a = 1
- // b = undefined
- // Array []
rest 参数只能写在最后,前面用... 标示,从运行结果可知,传入的参数先绑定 a , b, 多余的参数以数组形式交给变量 rest, 所以,
不在需要 arguments 我们就获取了全部参数。
如果传入的参数连正常定义的参数都没填满,也不要紧,rest 参数会接收一个空数组(注意不是 undefined)。
return 语句
前面我们讲到了 JavaScript 引擎有一个在行末自动添加分号的机制,这可能让你栽到 return 语句的一个大坑:、
- function foo() {
- return {
- name: 'foo'
- };
- }
- foo(); // {name:'foo'}
要注意:
- function foo() {
- return: //自动添加了分号,相当于return undefined
- {
- name: 'foo'
- }; // 这行语句已经没法执行到了。
- }
所以正确的多行写法是
- function foo(){
- return { // 这里不会自动加分号,因为表示语句尚未结束。
- name:'foo'
- }
- }
变量作用域
在 JavaScript 中,用 var 声明的实际上是有作用域的。
如果一个变量在函数体内部申明,则该变量的作用域为整个函数体,在函数体外不该引用该变量。
- 'use strict':
- function foo(){
- var x = 1;
- x = x +1;
- }
- x = x +2; // RefrenceError 无法在函数体外引用该该变量x
如果两个不同的函数各自申明了同一个变量,那么该变量只在各自的函数体内起作用。换句话说,不同函数内部的同名变量互相独立,互不影响:
- 'use struct':
- function foo(){
- var x = 1;
- x = x +1;
- }
- function bar (){
- var x= 'A';
- x = x + 'B';
- }
由于 JavaScript 的函数可以嵌套,此时,内部函数可以访问外部函数定义的变量,反过来则不行:
- 'use strict';
- function foo(){
- var x =1;
- function bar(){
- var x = 1;
- function bar(){
- var y= x +1; //bar 可以访问foo 的变量x
- }
- var z = y + 1; //RefernceError! foo 不可以访问bar 的变量y!
- }
- }
如果内部函数和外部函数的变量名重名怎么办?
- 'use strict':
- function foo(){
- var x = 1;
- function bar (){
- var x = 'A';
- alert('x in bar() =' + x); // 'A'
- }
- alert('x in foo()=' +x) //1
- bar();
- }
变量提升
JavaScript 的函数定义有个特点,它会先扫描整个函数体的语句,把所有申明的变量 "提升" 到函数顶部:
- 'use strict';
- function foo(){
- var x='Hello,'+y;
- alert(x);
- var y = 'Bob';
- }
- foo();
对于上述 foo() 函数,JavaScript 引擎看到的代码相当于:
- function foo(){
- var y; // 提升变量y的
- var x = 'Hello' + y;
- alert(x);
- y = 'Bob';
- }
由于 JavaScript 的这一怪异的 "特性",我们在函数内部定义变量时,请严格遵守 "在函数内部首先申明所有变量" 这一规则。最常见的做法是用一个 var 申明函数内部用到的所有变量:
- function foo(){
- var x =1, // x 初始化为1
- y = x +1, // y 初始化为2
- z,i; // z和i 为undefined
- // 其他语句
- for(i =0; i<100; i++){
- ...
- }
- }
全局作用域
不在任何函数内定义的变量就具有全局作用域,实际上,JavaScript 默认有一个全局作用域的变量实际上呗绑定到 window 的一个属性。
- 'use strict';
- var sourse = 'Learn JavaScript';
- alert(course); // 'Learn JavaScript';
- alert(window.course); // 'Learn JavaScript'
名字空间
全局变量会绑定到 window 上,不同的 JavaScript 文件如果使用相同的全局变量,或者定义了相同名字的顶层函数,都会造成
命名冲突,并且很难被发现,
减少冲突的一个方法是把自己的所有的变量和函数全部绑定到一个全局变量中。
- // 唯一的曲剧变量MYAPP
- var MYAPP = {};
- //其他变量:
- MYAPP.name = 'myapp';
- MYAPP.version = 1.0;
- // 其他函数
- MYAPP.foo = function (){
- return 'foo';
- };
把自己的代码全部放入唯一的名字空间 MYAPP 中,会大大减少全局变量冲突的可能。
局部作用域
由于 JavaScript 的变量作用域实际上是函数内部,我们在 for 循环等语句块中是无法定义具有无法定义具有局部作用域的变量的。
- function foo(){
- for(var i = 0; i<100; i++){
- //
- }
- i+=100; // 仍然可以引用变量;
- }
为了解决块级作用域,ES6 引入了新的关键字 let,用 let 替代 var 可以申明一个块级作用域的变量:
- function foo(){
- var sum = 0;
- for(let i=0; i<100;i++){
- sum +=i;
- }
- i +=1;
- }
常量
由于 var 和 let 声明的变量,如果要声明一个常量,在 ES6 之前是不行的,我们通常用全部大写的变量俩表示这是一个常量
不要修改他的值。
var PI = 3.14;
ES6 标准引入了新的关键字 const 来定义常量,const 与 let 都具有块级作用域;
const PI = 3.14;
PI = 3; // 某些浏览器不报错,但是无效果。
PI; // 3.14
以上这篇 JavaScript 基础函数_深入剖析变量和作用域就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持 phperz。
来源: http://www.phperz.com/article/17/0223/265665.html