标签:
函数是定义一次但可以调用或执行任意多次的一段JavaScript代码。
当一个函数在一个对象上被调用的时候,这个函数就叫做方法。 此时, 它的调用所在的对象就会作为函数的一个隐式的参数来传递。
// 函数 function func() { } var obj = { age: 18, getAge: function() { // 方法 } };
函数对任何语言来说都是一个核心的概念。通过函数可以封装任意多条语句,而且可以在任何地方、任何时候调用执行。 函数是JavaScript的"一等公民"。
// 函数声明的基本语法 // function关键字、funcname、小括号、大括号都是必需的 // 参数不是必需的 function funcname([arg1 [,arg2 [..., argn]]]) { statements } // 函数调用语法:函数名后面跟运算符()来调用 funcname();
1 函数声明不能出现在类似:if语句中(尽管不报错,但这是糟糕的做法!如果这么做了,容易出现混乱) 2 funcname是要定义的函数名,必须是一个标识符(不要使用保留字或关键字),而不能是字符串或者表达式。 3 包含在小括号中的0个或多个参数,参数用逗号隔开 4 大括号内的是函数主体,包含了多条语句 5 return语句,可有可无。 6 函数命名推荐使用帕斯卡命名法,即第一个单词后的所有单词都以一个大写字母开始。
function calcCircularArea(radius) { return Math.PI * radius * radius; } console.log(calcCircularArea(10));
//形参(函数声明时候的参数) function f(a, b) {} // a,b是形参,占位用,函数定义时形参无值 //实参(函数调用时候的参数) var x = 5, y = 6; f(x, y); //x,y实参,有具体的值,会把x,y复制一份给函数内部的a和b
解释: 1 JavaScript是弱类型的语言,可以用来保存任何类型的变量 2 即便你定义的函数只接收两个参数,在调用这个函数时也未必一定要传递两个参数。可以传递一个、三个甚至不传递参数 3 函数参数只能在函数体内使用 如果形参个数比实参个数多,那么多出来的参数值为:undefined(相当于声明变量,未初始化变量的值) 如果实参个数比形参个数多,那么多出来的参数将被忽略掉(不考虑 arguments) 4 JavaScript中的函数没有重载的概念(相对于其他强类型的语言来说, 例如:.Net、Java,并且这些语言中的函数,形参个数必须和实参个数相同) 补充: 所谓的重载是指:方法的签名(函数名称)相同,参数顺序、个数、类型不同
// 具有一个参数的函数 function addNum(num) { return num + 10; } // 具有两个参数的函数 function addNum(num1, num2) { return num1 + num2; } // 此时调用 addNum 会执行哪个? addNum(20 + 30); //三个参数的f1把两个参数的f1覆盖,调用的是三个参数的f1 //证明JavaScript中没有重载
function add(num1, num1) { console.log(num1); // ?? } add(1, 2);
// 求两个数中的最大值 function getMaxNum(num1, num2) { return num1 > num2 ? num1 : num2; } console.log(getMaxNum(10, 5)); // 求三个数中的最大值 function getMaxNum1(num1, num2, num3) { return getMaxNum(getMaxNum(num1, num2), num3); } console.log(getMaxNum1(5, 8, 3));
function getArrMaxNum(array) { var tempArr = array || [], i = 0, maxNum; if(tempArr.length === 0) { alert("请输入长度为1以上的数组"); return; } maxNum = tempArr[0]; for(; i < array.length; i++) { maxNum = array[i] > maxNum ? array[i] : maxNum; } return maxNum; } console.log(getArrMaxNum([1, 3, 9, 10, 5, 2]));
因为可以使用方括号语法访问它的每一个元素(即第一个元素是 arguments[0] ,第二个元素是 argumetns[1] ,以此类推),使用 length 属性来确定参数长度。 没有传递值的命名参数将自动被赋予 undefined 值,跟定义了变量但又没有初始化一样。
function testArguments() { console.log("第一个参数为:" + arguments[0]); } testArguments("123"); // 第一个参数为:123
function getMaxNum() { var len = arguments.length, maxNum = arguments[0], i = 0; if(len === 0) { return maxNum; } for (; i < len; i++) { maxNum = arguments[i] > maxNum ? arguments[i] : maxNum; } return maxNum; } var maxNum = getMaxNum(1, 3, 2, 9, 5, 8); console.log(maxNum); // ??
function b(x, y, a) { arguments[2] = 10; alert(a); } b(1, 2, 3); // ?? b(1, 2); // ??
函数的命名参数和arguments,它们的内存空间是独立的,而不是说读取这两个值会访问相同的内存空间。 只不过,它们的值会同步。 但是,如果调用时候有的参数未传入,那么通过修改 arguments 对应的值不会反映到命名参数中。
注意点: 1 如果return语句没有一个相关的表达式,它会返回undefined值 2 如果函数不包含return语句,它就只执行函数体中的每条语句,然后返回给调用者undefined
// 有return语句,但是什么都不返回 function returnNothing() { console.log("函数被调用了"); return; } var ret = returnNothing(); console.log(ret); // undefined // 没有return语句 function noRetrun() { console.log("函数被调用了"); } var ret = noRetrun(); console.log(ret); // undefined
// funcname 可以省略 var func = function funcname() { statements };
var func = function fun () { console.log(fun); // 函数体 }; func(); console.log(fun); // Uncaught ReferenceError: fun is not defined
var func = function factorial(num){ if (num <= 1) { return 1; } else { return num * factorial(num - 1); } }; console.log(func(4));
var fibonacci = function fib(num) { var result = 0; if(num === 1 || num === 2) { return 1; } result = fib(num - 1) + fib(num - 2); return result; }; console.log(fibonacci(3));
JavaScript中的函数,不只是一种语法(即,声明函数或函数表达式),还可以是数据, 这意味着能够把函数赋给变量、存储在数组的元素中或作为对象的属性,甚至作为函数参数传递
function add(x) { return x + 10; } var ret = add(8); // 18 var otherAdd = add; // 把add赋值为变量 otherAdd var otherRet = otherAdd(89); // 99 var arr = []; arr[0] = function(x) { return x + 10; }; arr[1] = 8; arr[0](arr[1]); // 18
// if() {} 语句块 var str = "window"; if(true) { var str = "if语句"; } console.log(str); // ?? // for 循环语句块 for(var i = 0; i < 10; i++) { // ... } console.log(i); // ? // 思考: 页面中有10个li,单击每个li弹出其索引号 // function 语句块 var str = "window"; function test() { var str = "local"; console.log(str); } test(); // ?? console.log(str);
结论:JavaScript函数形成了一个作用域
面试题:
var num = 1; function staticFunc() { console.log(num); // ?? } function test() { var num = 2; staticFunc(); } test();
JavaScript中的函数是通过词法来划分作用域的(而不是动态地划分作用域的)。 即,函数在定义它们的作用域运行(而不是在执行它们的作用域里运行)。
// 定义指的是这个语句被解析执行的时候 function func() { } // 执行指的是这个函数被调用的时候 func();
function func(num1, num2) { var sum = num1 + num2; return sum; } func(3, 6); // 复杂点的情况 var age = 18; var name = "Tom"; function getAge() { var age = 19; var str = "hello"; function getStr() { var str = "world"; console.log(age); // ?? console.log(name); // ?? console.log(str); // ?? } getStr(); } getAge();
解释: 当定义(声明)一个函数,当前的作用域链就保存起来,并且成为函数内部状态的一部分。 在最顶级,作用域链仅由全局对象组成,而并不和词法作用域相关。 然后,当定一个嵌套的函数时,作用域链就包含外围的包含函数。这意味着嵌套的函数可以访问包含函数的所有参数和局部变量。
执行环境定义了变量或函数有权访问的其他数据,决定了它们各自的行为。每个执行环境都有一个 与之关联的变量对象(variable object),环境中定义的所有变量和函数都保存在这个对象中。 虽然我们编写的代码无法访问这个对象,但解析器在处理数据时会在后台使用它。 全局执行环境是最外围的一个执行环境,在 Web 浏览器中,全局执行环境是 window 对象。 所有全局变量和函数都是作为 window 对象的属性和方法创建的 当代码在一个环境中执行时,会创建变量对象的一个作用域链(scope chain)。作用域链的用途,是 保证对执行环境有权访问的所有变量和函数的有序访问。 作用域链本质上是一个指向变量对象的指针列表,它只引用但不实际包含变量对象。 函数调用的执行过程: 1. 设置作用域链 当JavaScript的解释器调用一个函数,将此函数的作用域设置为定义函数的时候起作用的那个作用域链。 2. 创建调用对象(其他名称:激活对象、活动对象),添加到作用域链的前端 在作用域的前面添加一个新的对象:调用对象(call object),它用一个名为arguments的属性来初始化, 即:局部变量、函数参数和Arguments对象、内部函数都在函数内的作用域中,放到了调用对象里面。 这样就隐藏了作用域链更上层的任何同名的属性。 3. 调用结束后,调用对象从作用域链中移除。(一般情况下) 当没有涉及嵌套的函数的时候,作用域链是对调用对象的唯一引用。当对象从链中移除,也就是没有对它的引用了, 最终通过对它的垃圾收集而完结。 注意:this是一个关键字,而不是调用对象的一个属性,所以,this并不一定指的是函数自身。
function func() { console.log("很平常的一个函数"); } func(); // 嵌套的函数 function outerFunc(a, b) { function innerFunc(c) { console.log(a + "," + b + c); } innerFunc(); } outerFunc(); // 函数被作为返回值返回(了解) function foo() { var num = 10; return function() { console.log(num); }; } var num = 20; // bar引用了 foo 的内部函数 var bar = foo(); bar(); // ??
// 1. var foo = 1; function test(){ console.log(foo); // ?? var foo = 2; console.log(foo); // ?? } test();
// ?? func(); function func() { console.log("函数被调用了"); } // ?? func(); var func = function() { console.log("函数被调用了"); };
每个作用域都会进行提升操作。 包括变量和函数在内的所有声明都会在任何代码被执行前先被处理。 函数声明会被提升,但是函数表达式却不会被提升。 函数会首先被提升,然后才是变量 如果函数声明和变量声明的名称相同,此时,var foo; 尽管出现在 function foo(){} 的声明之前,但它是重复的声明(因此var foo;被忽略了),因为函数声明会被提升到普通变量之前。 如果是 var foo = 123; 即:声明同时初始化了,那么foo的值为123。
// 第一个小括号的作用是将函数声明转化为函数表达式 (function() { // 这里是模拟出来的块级作用域 })(); // 另外一种形式(了解) (function(){ }());
// 基本数据类型 function add(num) { num = num + 10; return num; } var count = 20; var result = add(count); console.log("count的值为:" + count); // ?? console.log("result的值为:" + result); // ?? // 复杂数据类型 // 1. function setAge(obj) { obj.age = 18; } var person = new Object(); person.age = 30; setAge(person); console.log(person.age); // ?? // 2. function setName(obj) { obj.name = "小明"; obj = new Object(); obj.name = "小红"; } var person = new Object(); person.name = "Jack"; setName(person); console.log(person.name); // ??
参数实际上是函数的局部变量,那么这些局部变量(包括:函数参数、函数内部声明的变量), 在函数执行完毕后会被立即销毁掉。
练习:输入某年某月某日,判断这一天是这一年的第几天?
// 创建一个对象 var obj = new Object(); // 给对象设置属性 obj.name = "小明"; obj.age = 18;
JavaScript中的对象:无序属性的集合,其属性可以包含基本值、对象或者函数。 严格来讲,这就相当于说对象是一组没有特定顺序的值。对象的每个属性或方法都有一个名字, 而每个名字都映射到一个值。
对象与数字、字符串、布尔值这样的数据类型不同,它们不是表示一个单个的值,而是值的集合。 对象是已命名值的一个集合。 数字、字符串、布尔值是简单数据类型 对象是复杂数据类型
// 使用 new 操作符,后面跟 Object构造函数调用,来创建对象 // 此时,返回值:obj,就是新创建出来的对象 var obj = new Object(); // 给对象添加属性 obj.name = "静静"; obj.age = 18; obj.getName = function () { return this.name; // 静静 };
// 语法规则 var person = { // 属性名: 属性的值 // 使用逗号来分隔不同的属性(如果当前属性后面还有其他属性的话) name: "Nicholas", age: 18 }; // 创建对象设置属性 var person = { name: "加引号的属性名称", 0: "属性名为数值类型,那么这里的数值属性名会自动转换为字符串" }; // 创建一个空对象,没有任何属性 var person = {};
1. 对象字面量是对象定义的一种简写形式,目的在于简化创建包含大量属性的对象的过程。 2. 语法规则中,左边的花括号({)表示对象字面量的开始,因为它出现在了表达式上下文中。 3. 对象字面量的属性名,可以不加引号 也可以加引号(单引号或者双引号都可以)
// 创建一个空对象 var obj = {}; // 使用点运算符(.)来设置属性的值 obj.name = "这是name属性的值"; obj.age = 18; // 给age属性设置值:18 // 使用方括号[]来设置属性的值,注意:要访问的属性是字符串的形式或者是变量 obj["font-size"] = "30px";
// 获取属性的两种方式 console.log(obj.name); console.log(obj["name"]);
区别: 1. 都可以用来设置或者获取对象的属性。 2. 方括号([])可以通过变量来访问属性。 3. 属性名中包含会导致语法错误的字符,或者属性名使用的是关键字和保留字,使用方括号表示法。 例如:obj["first name"],由于"first name"中包含一个空格,所以不能使用点表示法来访问它。 然后,属性名中是可以包含非字母非数字的,这时候就可以使用方括号表示法来访问它们。 4. 除非必须使用变量来访问属性,否则推荐使用点表示法。
var obj = { name: "明哥", age: 18, weight: 50 }; for(var ob in obj) { // ob 为属性名 // obj[ob] 为属性名对应的值 console.log("属性 " + ob + " 的值为:" + obj[ob]); }
function Person(name, age) { this.name = name; this.age = age; } var p = new Person("猴哥", 18); console.log(p.name); console.log(p.age);
// 声明一个包含10个参数的函数 function moreArgsFunc(arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10) { // 函数处理 } // 调用函数 moreArgsFunc(true, false, 1, 0, 1, "string", "number", " ", null, null);
function objArgsFunc(obj){ // 函数处理 // 使用参数 obj.arg1 } obj({ /*arg1: true, arg2: false, ...*/ });
// 1 作为函数调用 function func() { this.age = 18; console.log(this.age);// this 指向 window } func(); // 2 作为对象方法调用 var obj = { name: "functionName", age: 19, getName: function() { console.log(this.name);// this指向obj } }; obj.getName(); // 3 作为构造函数调用 function Person(name) { this.name = name;// this 指向新创建出来的对象p } var p = new Person("小明"); console.log(p.name); // 4 使用call或者apply调用 // call和apply的区别:参数不同 // call(上下文, arg1, arg2, ...) // apply(上下文,[arg1, arg2, ...]) func.call(obj); // this 指向传入的参数:obj
标签:
原文地址:http://blog.csdn.net/chizhongwenshan/article/details/51014871