js函数2
caller属性:caller属性并不是arguments对象的,而是函数对象本身的属性,它显示了函数的调用者,如果函 数是在全局执行环境中(浏览器中)被调用,那么它的值为null,如果在另一个函数中被调用,它的 值就是那个函数。
全局执行环境中被调用:
浏览器中
vbody>
<script>
let test = function(){
console.log(test.caller);
}
test();//null
v/script>
v/body>
node 中
let test = function(){
console.log(test.caller);
}
test();//[Function]
被一个函数所调用:
let test = function(){
let test2 = function(){
console.log(test2.caller);
//[Function: test]
//因为这个函数的调用者就是test函数
}
test2();
}
test();
callee属性:callee是argument s对象的一个属性,该属性是一个指针,指向拥有这个argument s对象的函数
let test = function(){
let test2 = function(){
let test3 = function(){
console.log(arguments.callee);
//[Function: test3]
}
test3();
}
test2();
}
test();
callee的作用在于能够找到argument s对象所属的函数,不让函数的执行和函数名仅仅的关联在 —起,我们来看下面这个例子:
//计算阶乘的递归函数
let test = function(i){
if(i == 1){
return 1;
}else{
return i * test(i-]);//这里就和函数名蜜貞的关联7■起来
}
}
console.log(test(3));
如果我们把上面的写法稍作修改,就可以看到上面写法的缺陷
//计算阶乘的递归函数
let test = function(i){
if(i == 1){
return 1;
}else{
return i * test(i-]);//这里就和函数名蜜貞的关联7■起来
}
}
let test2 = test; //将阶乘函数赋值给test2
/ /改变test这个阶乘函数的函数体
test = function(){
console.log("我已经改变了");
console.log(test2(3));
//我已经改变了
/ / NaN
所以,这个时候就可以使用arguments对象的callee属性来降低这种关联
//计算阶乘的递归函数
let test = function(i){
if(i == 1){
return 1;
}else{
return i * arguments.callee(i-l); //callee 指向拥有arguments 对象的函数 }
}
let test2 = test; //将阶乘函数赋值给test2
/ /改变test这个阶乘函数的函数体
test = function(){
console.log(” 我已经改变了");
}
console.log(test2(3));//
箭头函数
所谓箭头函数,是从ES6开始新增加的一种声明函数的方式。其最大的特点在于不需要function 关键字,取而代之的是使用一个=> 来进行表示。箭头函数的基本语法如下:
let变量=(形式参数)=> {
//函数体
}
箭头函数示例:
let test = (name) => {
console.log("Hello",name);
}
test("xiejie");//Hello xiejie
上面所介绍的,只是箭头函数的基本写法。实际上箭头函数根据形式参数和函数体的不同,书写 的方式拥有一些变形。如下:
//如果没有参数
let 变量=()=> {
//函数体
}
//如果只有一个形参
let变量=形参=> {
//函数体
}
//如果函数体只有一个返回值
let 变量=形参=> expression
例如:书写求立方根的箭头函数(当然这里只是练习,ES6已经提供了求寫的方式,使用**)
let test = x => x*x*x; console.log(test(3));//27
变量提升
所谓变量提升,就是指在使用var关键字进行变量声明的时候,默认会将声明变量的部分提升至 当前作用域的最顶上,但是注意提升的只有变量的声明部分,赋值是不会提升的
console.log(i);//undefined
var i = 10;
console.log(i);//10
还有一点要注意的是,只有var声明的变量才具有这种特性,let或者const不存在变量提升
console.log(i);//ReferenceError: i is not defined
let i = 10;
如果我们在函数里面声明变量时没有添加关键字,那么默认将会是在全局环境中声明一个变量
let test = function(){
i = 10;
}
test();
console.log(i);//10
通过上面的代码我们可以证明这是一个全局作用域里面的变量,但是这个变量究竟是以var的形 式声明的还是以let或者说const的方式声明的呢?
答案就是:以var的形式进行声明的,但是不具有变量提升。
这里我们可以证明这一点,在上面的例子中我们在外部成功访问到了在函数里面没有添加关键字
而声明的变量i ,接下来我们来提前打印输出这个i变量,如下:
console.log(i);//ReferenceError: i is not defined
let test = function(){
i = 10;
}
test();
可以看到这里会报错,显示"i is not defined",从而证明了不具有变量提升的特性。
函数提升
所谓函数提升,是指当我们使用字面量方式来声明一个函数的时候,此时函数的声明会提升到当 前作用域的最顶端,这意味着我们可以将函数的调用书写到函数的声明之前
test();//Hello!
function test(){
console.log("Hello!");
}
//等价于
// test : pointer to test()
// test()
// function test()
// {
// console.log("Hello!");
// }
需要注意的是,仅仅只有普通函数声明的时候才存在函数提升,如果是使用函数表达式来进行的 函数声明,则不存在有函数提升的情况
test();//Hello!
let test = function(){
console.log("Hello!");
}
//ReferenceError: test is not defined
还有一点就是变量提升和函数提升是可以同时存在的。上面的例子中如果我们声明函数时使用的 是var关键字的话,那么同样存在变量的提升,如下:
console.log(test);//undefined
var test = function(){
console.log("Hello!");
}
console.log(test);//[Function: test]
回调函数
函数在JavaScript中是一等公民。这里所谓的一等公民,就是指函数可 以像其他数据类型一样作为函数的参数传入,也可以通过返回值的形式来返回。这里要介绍的回 调(callback)就是利用了这一特性,我们将传递给另一个函数作为实参的函数称之为回调函数 (callback) o
常见回调函数介绍
实际上回调函数我们在之前的学习中就已经见到过了。就在我们使用sort。为数组进行排序的时 候,默认是使用的ASCII码来进行的排序。如果想要按照数值来进行排序,就需要我们传递一个 回调函数进去。这里我们可以来复习一下:
let arr = [0,12,3,7,-12,23];
console.log(arr.sort(function(a,b){
return a - b;
//降序就返回b - a
}));
甚至我们还可以使用前面小节所介绍过的箭头函数,将上面的排序写作如下:
let arr = [0,12,3,7,-12,23]; console.log(arr.sort((a,b) => a - b));
在JavaScript里面,除了上面所介绍的sort。以外,还有诸如forEach。,map。,every。,some。 等函数,也是所常见的回调函数。
迭代方法
every。:对数组的每一项运行给定的函数,如果该函数每一项都返回true,则返回true
let arr = [1,2,3,4,5,6,7,8,9,10];
//将数组的每一项传入到回调函数,如果每一项返回true,那么最终返回true
let i = arr.every(function(item){ if(item % 2 == 0){
return true;
}else{
return false;
}
});
console.log(i);//false
与every。比较相似的是s ome。,该方法可以对数组的每一项运行指定的函数,如果该函数只要有 —项返回true则返回true
let arr = [1,2,3,4,5,6,7,8,9,10];
//将数组的每一项传入到回调函数,如果有一项返回true,那么最终返回true
let i = arr.some(function(item){ if(item % 2 == 0){
return true;
}else{
return false;
}
});
console.log(i);//true
filterO: filter是过滤的意思,所以这个方法会返回一个数组,数组里面是返回true的元素
let arr = [1,2,3,4,5,6,7,8,9,10];
//将数组的每一项传入到回调函数,然后将返回为true的项目组成一个数组
let i = arr.filter(function(item){
if(item % 2 == 0){
return true;
}else{
return false;
}
});
console.log(i);//f 2, 4, 6, 8, 10 ]
forEachO:这个方法我们在前面介绍数组遍历的时候,就已经见到过了。该方法就是简单的将数 组每一项传入到函数,然后执行该函数里面的代码。需要注意一下的是,该回调函数没有返回 值。
let arr = [1,2,3,4,5,6,7,8,9,10];
//将数组的每一项传入到回调函数,然后执行回调函数里面的操作
let i = arr.forEach(function(item){
console.log(item);
});
console.log(i);//undefined
map。:对数组的每一项运行test函数,返回一个数组,这个数组是每次调用函数后的运行结果
let arr = [1,2,3,4,5,6,7,8,9,10];
//将数组的每一项传入到回调函数,然后将返回的结果组成一个新的数组返回
let i = arr.map(function(item){
if(item % 2 == 0){
return true;
}else{
return false;
}
});
console.log(i);
//[ false, true, false, true, false, true, false, true, false, true ]
注意:以上方法都不会改变原数组的值,并且都可以接收两个参数,第一个是数组的元素
值,第二个是数组的索引。上面的例子中我们都只接收了一个参数,即数组的值。