简介
作用域和闭包相关参考:Javascript之作用域和闭包
变量提升和函数声明相关参考:Javascript之函数声明和变量提升
函数名实际上也是一个指向函数对象的指针,不会与某个函数绑定
一个函数可能会有多个名字
// 写法一:函数声明(推荐写法)
function sum (num1, num2) {
return num1 + num2;
}
// 写法二:函数表达式(推荐写法)
var sum = function(num1, num2) {
return num1 + num2;
};
// 写法三:Function 构造函数(不推荐写法)
var sum = new Function("num1", "num2", "return num1 + num2");
- 使用函数表达式时,注意函数名
var test = function test1() {
var a = 1;
console.log(a);
}
test() // 1
test1() // ReferenceError: test1 is not defined
/**
* 相当于:
* var test = function () {
* var a = 1;
* console.log(a);
* }
*/
- 函数体内参数是沿着作用域链依次往外找的
a = 1;
function test1() {
var b = 2;
console.log(a) // test1里面没有a,往上找,找到全局的 a = 1
function test2() {
var c = 3
console.log(b); // test2里面没有b,往上找,找到test1里面的 b = 2
}
test2();
console.log(c); // test1里面没有c,往上找,全局也没有。报错:ReferenceError: c is not defined
}
test1();
参数
-
arguments
是一个类数组对象,包含着传入函数中的所有参数。 -
arguments
还有一个名叫callee
的属性,该属性是一个指针,指向拥有这个arguments
对象的函数 -
this
引用的是函数据以执行的环境对象 -
当在网页的全局作用域中调用函数时,
this
对象引用的就是window
function factorial(num){
// arguments 是一个类数组对象
console.log(typeof(arguments)); // object
if (num <=1) {
return 1;
} else {
// return num * factorial(num-1) // 直接用函数名当然也可以,可以使用arguments.callee消除耦合
return num * arguments.callee(num-1)
}
}
形参:形式参数,是函数作用域内的变量。
实参:调用函数时,传入的参数,有对应的形参时,实参会赋值给形参。
形参可以设置默认值,当有对应实参赋值的时候,默认值一般会被实参替换。
若形参设置了不为undefined
的默认值,传入对应实参为undefined
时,形参不会被实参替换。
function test1 (a, b) {
console.log(test1.length) // 形参的长度 2
console.log(arguments.length) // 实参的长度 3
}
test1(1,2,3)
/**
* 形参:a, b
* 实参:1, 2, 3 arguments是实参集合,是一个object对象(类数组)
* 形参的数量使用: 方法名.length 获取
* 实参的数量使用: arguments.length 获取
*/
- 非严格模式下,
arguments
值可修改
function test2(a, b) {
a = 3;
console.log(a, arguments[0]) // 3 3
}
test2(1,2)
/**
* 实参第一个参数传入1, 形参使用 a 接收 -> a = 1
* a = 3 ---> arguments[0] 也跟着改了?
*/
function test3(a = 5, b) {
a = 3;
console.log(a, arguments[0]) // 3 1
}
test3(1,2)
/**
* a 有默认值的时候 a = 3 ---> arguments[0] 为啥不跟着改了?默认严格模式?
*/
- 实参数量不够时,后面的形参默认是
undefined
function test4(a, b) {
b = 3;
console.log(b, arguments[1]); // 3 undefined
}
test4(1);
/**
* 实参只有一个,所以 b = undefined
* b = 3 ---> 因为没有arguments[1] 所以b修改不会引起arguments[1]的变化
*/
ES5
和ES6
默认参数
// 默认参数 ES5
function test(a, b) {
a = arguments[0] || 1
b = arguments[1] || 1
console.log(a) // arguments[0] = undefined ---> a = 1
console.log(b) // arguments[1] = 2 ---> b = 2
}
test(undefined, 2)
// 默认参数 ES6
function test(a = 1, b = 1) {
console.log(a) // 实参是 undefined,使用默认参数 1
console.log(b) // 实参是 2 --> b = 2
}
test(undefined, 2)
- 非严格模式下的
arguments.callee
function test6(a,b,c){
console.log(arguments.callee.length) // 3
console.log(arguments.callee === test6) // true
}
test6(1,2,3, 4);
ES6
参数的解构赋值和默认值
function add([x, y]){
return x + y;
}
console.log(add([1, 2])) // 3
function move1({x = 0, y = 0} = {}) {
return [x, y];
}
move1({x: 3, y: 8}); // [3, 8]
move1({x: 3}); // [3, 0]
move1({}); // [0, 0]
move1(); // [0, 0]
function move2({x, y} = { x: 0, y: 0 }) {
return [x, y];
}
move2({x: 3, y: 8}); // [3, 8]
move2({x: 3}); // [3, undefined]
move2({}); // [undefined, undefined]
move2(); // [0, 0]
立即执行函数
,
分割的,取后面的值
var fn = (
function test1(){
return 1;
},
function test2(){
return '2';
}
)();
console.log(typeof(fn)) // string
console.log((1, 2))
/**
* ,分割的,取后面的值
* var fn = (function test2(){
* return '2';
* })();
* 自动执行 ---> var fn = '2'
* typeof(fn) ---> string
*/
闭包
能够访问另一个函数作用域的变量的函数
用途:
- 可以读取函数内部的变量,
- 让这些变量的值始终保持在内存中
function outer() {
var a = '变量1'
return function() { // 该函数能够访问到outer函数作用域下的a
console.log(a)
}
}
箭头函数
-
函数体内的
this
对象,就是定义时所在的对象,而不是使用时所在的对象 -
不可以当作构造函数,也就是说,不可以使用
new
命令,否则会抛出一个错误 -
不可以使用
arguments
对象,该对象在函数体内不存在。如果要用,可以用rest
参数代替
关卡
- 经典题:输出代码
function test() {
var arr = []
for(var i = 0; i < 10; i++) {
arr[i] = function() {
console.log(i)
}
}
return arr
}
var myArr = test()
for(var i = 0; i < 10; i++) {
myArr[i]() // 10个10
}
/**
* var i,i 的作用域在 test 内
* 执行完 test 之后(i = 10), arr数组里面的每一项都是 function() { console.log(i) }
* 所以输出10
*/
// 改写test()方法,使之输出 0 - 9
// 方法一:var i = 0 → let i = 0
function test() {
var arr = []
for(let i = 0; i < 10; i++) {
arr[i] = function() {
console.log(i)
}
}
return arr
}
/**
* let i,i 的作用域在 for 内
* function() { console.log(i) } 用到的 i 是 for循环作用域内的 i
* 输出 0 - 9
*/
// 方法二:使用闭包
function test(){
var arr = []
for(var i = 0; i < 10; i++) {
(function(j) {
arr[j] = function(){
console.log(j)
}
})(i)
}
return arr
}
/**
* 使用闭包包裹,使用的 i 是每次循环传入的 i
* 输出:0 - 9
*/
// 方法三:使用闭包,可以只包裹function
function test(){
var arr = []
for(var i = 0; i < 10; i++) {
arr[i] = (function(j) {
return function() {
console.log(j)
}
})(i)
}
return arr
}
- 合并任意个数的字符串
var concat = function(){
var result = '';
for(var i = 0; i < arguments.length; i ++){
result += arguments[i];
}
return result;
}
console.log(concat('st','on','e')); // stone
- 输出指定位置的斐波那契数列
var fioacciSequece = function(count){
return (function(n) {
if (n <= 1) {
return n;
} else {
return arguments.callee(n - 1) + arguments.callee(n - 2);
}
}(--count));
}
console.log(fioacciSequece(12)); // 0、1、1、2、3、5、8、13、21、34、55、[89]
- 三维数组或
n
维数组去重,使用arguments
重写
var arr = [2,3,4,[2,3,[2,3,4,2],5],3,5,[2,3,[2,3,4,2],2],4,3,6,2];
var unique = function(arr) {
var result = [];
(function(arr) {
var f = arguments.callee;
arr.forEach(function() {
if (Array.isArray(arguments[0])) {
f(arguments[0]);
} else {
if (result.indexOf(arguments[0]) < 0) {
result.push(arguments[0]);
}
}
});
}(arr));
return result;
}
console.log(unique(arr)); // [2,3,4,5,6]
发表评论