js函数进阶

函数进阶

函数的定义方式

  1. 命名函数
function foo () {}
  1. 匿名函数
var foo = function(){}

函数的调用方式

  1. 普通函数
function foo () {
    console.log("普通函数的调用")
}
foo();//foo.call()
  1. 构造函数
function Person(){
    
}
var p = new Person()
  1. 对象方法
var obj = {
    saiHi:function(){
        console.log("hi")
    }
}
obj.saiHi()
  1. 绑定事件函数
btn.onclick = function(){}
  1. 定时器函数
setInterval(function(){
    
},1000)
  1. 立即执行函数
(function(){})();

函数内 this 指向的不同场景

函数的调用方式决定了 this 指向的不同:

调用方式非严格模式备注
普通函数调用window严格模式下是 undefined
构造函数调用实例对象原型方法中 this 也是实例对象
对象方法调用该方法所属对象紧挨着的对象
事件绑定方法绑定事件对象
定时器函数window
立即执行函数window

这就是对函数内部 this 指向的基本整理,写代码写多了自然而然就熟悉了。

apply方法

apply() 方法调用一个函数, 其具有一个指定的 this 值,以及作为一个数组(或类似数组的对象)提供的参数。

注意:该方法的作用和 `call()` 方法类似,只有一个区别,就是 `call()` 方法接受的是若干个参数的列表,而 `apply()` 方法接受的是一个包含多个参数的数组。

语法:

fun.apply(thisArg, [argsArray])


参数:

  • thisArg
  • argsArray

apply()call() 非常相似,不同之处在于提供参数的方式。
apply() 使用参数数组而不是一组参数列表。例如:

fun.apply(this, ['eat', 'bananas'])

function foo(arr){
    console.log(this);//window
    console.log(arr);//打印的不是数组,是字符串pink
}

foo.apply(null,['pink'])
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    <script>
        var arr = [3, 42, 31, 3, 5, 465, 6, 99]
        function foo(arr) {
            console.log(this);//window
            console.log(arr);//打印的不是数组,是字符串pink
        }

        foo.apply(null, ['pink'])

        var max = Math.max.apply(null, arr)
        console.log(max);

    </script>
</body>

</html>
bind

bind() 函数会创建一个新函数(称为绑定函数),新函数与被调函数(绑定函数的目标函数)具有相同的函数体(在 ECMAScript 5 规范中内置的call属性)。
当目标函数被调用时 this 值绑定到 bind() 的第一个参数,该参数不能被重写。绑定函数被调用时,bind() 也接受预设的参数提供给原函数。
一个绑定函数也能使用new操作符创建对象:这种行为就像把原函数当成构造器。提供的 this 值被忽略,同时调用时的参数被提供给模拟函数。

语法:

fun.bind(thisArg[, arg1[, arg2[, ...]]])


参数:

  • thisArg
    • 当绑定函数被调用时,该参数会作为原函数运行时的 this 指向。当使用new 操作符调用绑定函数时,该参数无效。
  • arg1, arg2, …
    • 当绑定函数被调用时,这些参数将置于实参之前传递给被绑定的方法。

返回值:

返回由指定的this值和初始化参数改造的原函数拷贝。

示例1:

this.x = 9; 
var module = {
  x: 81,
  getX: function() { return this.x; }
};

module.getX(); // 返回 81

var retrieveX = module.getX;
retrieveX(); // 返回 9, 在这种情况下,"this"指向全局作用域

// 创建一个新函数,将"this"绑定到module对象
// 新手可能会被全局的x变量和module里的属性x所迷惑
var boundGetX = retrieveX.bind(module);
boundGetX(); // 返回 81

示例2:

function LateBloomer() {
  this.petalCount = Math.ceil(Math.random() * 12) + 1;
}

// Declare bloom after a delay of 1 second
LateBloomer.prototype.bloom = function() {
  window.setTimeout(this.declare.bind(this), 1000);
};

LateBloomer.prototype.declare = function() {
  console.log('I am a beautiful flower with ' +
    this.petalCount + ' petals!');
};

var flower = new LateBloomer();
flower.bloom();  // 一秒钟后, 调用'declare'方法

小结
  • call 和 apply 特性一样
    • 都是用来调用函数,而且是立即调用
    • 但是可以在调用函数的同时,通过第一个参数指定函数内部 this 的指向
    • call 调用的时候,参数必须以参数列表的形式进行传递,也就是以逗号分隔的方式依次传递即可
    • apply 调用的时候,参数必须是一个数组,然后在执行的时候,会将数组内部的元素一个一个拿出来,与形参一一对应进行传递
    • 如果第一个参数指定了 null 或者 undefined 则内部 this 指向 window
  • bind
    • 可以用来指定内部 this 的指向,然后生成一个改变了 this 指向的新的函数
    • 它和 call、apply 最大的区别是:bind 不会调用
    • bind 支持传递参数,它的传参方式比较特殊,一共有两个位置可以传递
        1. 在 bind 的同时,以参数列表的形式进行传递
        1. 在调用的时候,以参数列表的形式进行传递
      • 那到底以谁 bind 的时候传递的参数为准呢还是以调用的时候传递的参数为准
      • 两者合并:bind 的时候传递的参数和调用的时候传递的参数会合并到一起,传递到函数内部

高阶函数

  • 函数可以作为参数
  • 函数可以作为返回值
作为参数
function sum(a,b,callback){
    console.log(a+b);
    callback && callback();//有参数才调用
}

sum(1,2,function(){
    console.log("求和之后调用的!")
})
作为返回值
function foo(){
    return function(){}
}
foo()

函数闭包

什么是闭包

闭包就是能够读取其他函数内部变量的函数,
由于在 Javascript 语言中,只有函数内部的子函数才能读取局部变量,
因此可以把闭包简单理解成 “定义在一个函数内部的函数”。
所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。

闭包的用途:

  • 可以在函数外部读取函数内部成员
  • 让函数内成员始终存活在内存中
一些关于闭包的例子

示例1:

function foo() {
    var num = 10
    function get() {
        console.log(10)
    }
    return get;
}
var get = foo();
get()   //获取到get里的值,相当于在外作用域访问到foo函数里面的值


//简写
function foo() {
    var num = 10
    return function () {
        console.log(10)
    };
}
var get = foo();
get()
闭包的思考题
  • 有没有闭包的产生
  • 结果是什么?

思考题 1:

var name = "The Window";
var object = {
  name: "My Object",
  getNameFunc: function () {
    return function () {
      return this.name;
    };
  }
};

console.log(object.getNameFunc()())//The Window
//object.getNameFunc()() => var f = object.getNameFunc()   f()  this指向的是window  全局属性是挂载到window下的
//没有闭包的产生,函数内没有局部变量

思考题 2:

var name = "The Window";  
var object = {    
  name: "My Object",
  getNameFunc: function () {
    var that = this;//此时的this指向的是调用者=》object
    return function () {
      return that.name;
    };
  }
};
console.log(object.getNameFunc()())//My Object,有闭包的产生,使用了局部变量

小结
  • 闭包是一个函数,一个作用域可以访问另外一个函数的局部变量

函数递归

  • 函数内部自己调用自己,这个函数就是递归
  • 作用和循环一样,但是容易发生死循环(栈溢出)
递归执行模型
function fn(){
    fn()
}
fn()//死递归

必须加退出条件

var num = 1;
function fn(){
    console.log("我是递归")
    if(num>10){
        return;
    }
    num++;
    fn()
}
fn()

举个栗子:

计算阶乘的递归函数
//求1到n的阶乘  1*2*3...*n

function fn (num) {
  if (num <= 1) {
    return 1
  } else {
    return num * fn(num - 1)
  }
}

console.log(fn(3))

//详细思路
//1、return 3*fn(3-1)
//2、return 3*fn(2)  此时必须返回的是结果 不能是函数
//3、继续调用 return 3*(2*fn(2-1))
//4、return 3*(2*fn(1))
//5、return 3*(2*1)
//6、return 3*(2)
//结果 6
斐波那契数列
// 1、1、2、3、5、8
// 用户输入一个数字 n 求出这个数字对应的序列值
// 我们只需要知道用户输入的n,那么前面两项(n-1  n-2)
function fb(n) {
    if(n==1 || n==2){
        return 1;
    }
    return fb(n-1)+fb(n-2);
}

console.log(fb(3));//2
console.log(fb(6));//8
根据输入的id,返回数据对象

数据

var data = [{
    id:1,
    name:'家电',
    goods:[{
        id:11,
        gname:'冰箱'
    },{
        id:12,
        gname:'洗衣机'
    }]
},{
    id:2,
    name:'服饰'
}]
//我们想要输入id,就可以获取数据对象
//forEach遍历
function getId(json, id) {
    json.forEach(element => {
        if(element.id == id){
            console.log(element);
        }else if(element.goods && element.goods.length>0){
            //里面有goods 这个数组 并且 数组的长度不为0
            getId(element.goods,id)
        }
    });
}

getId(data, 1);
getId(data, 12);

不打印,而是获取数据

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    <script>
        var data = [{
            id: 1,
            name: '家电',
            goods: [{
                id: 11,
                gname: '冰箱'
            }, {
                id: 12,
                gname: '洗衣机'
            }]
        }, {
            id: 2,
            name: '服饰'
        }]
        //我们想要输入id,就可以获取数据对象
        //forEach遍历
        function getId(json, id) {
            var o ={};
            json.forEach(element => {
                if(element.id == id){
                    // console.log(element);
                    o = element;
                    return o;
                }else if(element.goods && element.goods.length>0){
                    //里面有goods 这个数组 并且 数组的长度不为0
                   o =  getId(element.goods,id)
                }
            });

            return o;
        }

       console.log(getId(data, 1));
       console.log(getId(data, 12));
    </script>
</body>

</html>

浅拷贝和深拷贝

  1. 浅拷贝只是拷贝一层,更深层次对象级别的只拷贝引用(地址值)
  2. 深拷贝拷贝多层,每一级别的数据都会拷贝

浅拷贝

//浅拷贝
        var data = {
            name:'tom',
            sex:"男"
        }
        var o = {}
        for(var k in data){
            o[k] = data[k]
        }
        console.log(o);
        

        var data1 = {
            name:'tom',
            sex:"男",
            msg:{
                age:20
            }
        }
        var o1 = {}
        for(var k1 in data1){
            o1[k1] = data1[k1]
        }
        console.log(o1);//age是20,但是浅拷贝 拷贝是地址值,修改o1的年龄,data1也随之修改
        o1.msg.age = 100
        console.log(data1);//100
        

在这里插入图片描述

ES6新语法
var data1 = {
    name:'tom',
    sex:"男",
    msg:{
        age:20
    }
}
var o1 = {}
Object.assign(o1,data1)
console.log(o1);//age是20,但是浅拷贝 拷贝是地址值,修改o1的年龄,data1也随之修改
o1.msg.age = 100
console.log(data1);//100

深拷贝

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ATC314nf-1576397400282)(函数进阶/image-20191207192318701.png)]

var data = {
    name: 'tom',
    sex: "男",
    msg: {
        age: 20
    },
    color:['pink','red']
}
var o = {}

function deepCopy(newObj, oldObj) {
    for(var k in oldObj){
        //如果是简单类型 直接拷贝,复杂类型进入后在拷贝
        //获取属性值oldObj[k]
        var item = oldObj[k]
        if(item instanceof Array){
            newObj[k] = []
            deepCopy(newObj[k],item)
        }else if(item instanceof Object){
            newObj[k] = {}
            deepCopy(newObj[k],item)
        }else{
            newObj[k] = item;
        }
    }
}

deepCopy(o,data);
console.log(o);

o.msg.age =100;
console.log(data);//没有被修改

递归应用场景

  • 深拷贝
  • 菜单树
  • 遍历 DOM 树

wx:410473720

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值