JS高级-函数

目录

 一、函数

 (一)函数的定义方式

(二)函数的调用方式

(三)函数内this的指向

(四)修改函数内this指向

1、call方法

2、apply方法

3、bind方法

(五)高阶函数

(六)闭包

1、概念

2、作用

3、实际运用

(七)递归函数

(八)浅拷贝 & 深拷贝

1、浅拷贝概念

 2、浅拷贝语法糖

3、深拷贝概念

二、原型与原型链

(一)函数的prototype 

(二)显示原型与隐式原型

(三)原型链

(四)探索instanceof

(五)原型对象的this指向问题

三、继承

1、call()

2、借用构造函数继承父类型属性


 

 一、函数

 (一)函数的定义方式

        1、函数声明方式function关键字(命名函数)

        2、函数表达式(匿名函数)

        3、new Function()

var fn = new Function('参数1', '参数2', ..., '函数体')
  •  Function里面参数都必须是字符串格式
  • 第三种方式执行效率低,也不方便书写,因此使用较少
  • 所有函数都是Function的实例(对象)
  • 函数也属于对象

(二)函数的调用方式

        1、普通函数                     fn()

        2、对象的方法                 obj.fn()

        3、构造函数                    new Star()

        4、绑定事件函数             点击按钮

        5、定时器函数                 定时结束

        6、立即执行函数(IIFE)    自主执行

(三)函数内this的指向

这些this的指向,是当我们调用函数的时候确定的。调用方式的不同决定了this的指向不同

调用方式this指向
普通函数调用

window

构造函数调用实例对象 原型对象里面的方法也指向实例对象
对象方法调用该方法所属对象
事件绑定方法绑定事件对象
定时器函数window
立即执行函数window

(四)修改函数内this指向

JavaScript为我们专门提供了一些函数方法来帮我们更优雅地处理函数内部this的指向问题,常用的有bind()、call()、apply()三种方法

1、call方法

        call()方法调用一个对象,简单理解为调用函数的方式,但是它可以改变函数的this指向。

        fn.call(thisArg, arg1, arg2, ...)

2、apply方法

        apply()方法调用一个函数。简单理解为调用函数的方式,但是它可以改变函数的this指向。

/**
     *  fn.apply(thisArg, [argsArray])
     *  thisArg:在fu函数运行时指定的this值
     *  argsArray:传递的值,必须包含在数组里面
     *  返回值就是函数的返回值,因为它就是调用函数
     * */
    var o = {
      name: 'Bob'
    };
    function fn() {
      console.log(this);
    };
    fn.apply(o);

    // 主要应用:进行数组的一些操作
    var arr = [1, 66, 3, 98, 4];
    var result = Math.max.apply(null, arr);    //调用Math库中的方法,this设为null,将数组作为参数传递进去
    console.log(result);

3、bind方法

        bind()方法不会调用函数,但能改变函数内部的this指向

/**
     *  fn.bind(thisArg, arg1, arg2, ...)
     *  thisArg:在fu函数运行时指定的this值
     *  arg1, arg2:传递的其他参数
     *  返回值就是指定的this值和初始化参数改造的原函数拷贝
     * */
    // 1基础
    var o = {
      name: 'Bob'
    };
    function fn() {
      console.log(this);
    }
    var fun = fn.bind(o);   //只改this,不调用函数,返回一个改完this的拷贝函数
    fun()                   //调用拷贝函数
    // 2运用
    var btn = document.querySelector('button');
    btn.onclick = function () {
      this.disabled = true;
      setTimeout(function () {
        this.disabled = false;    //由于定时器中的this指向的是window,而此处需要的this是btn的this,因此需要利用bind将定时器内部的this指向改为外部this的指向
      }.bind(this), 2000);
    }

(五)高阶函数

 高阶函数是对其他函数进行操作的函数,它接受函数作为参数或将函数作为返回值输出。

        (例如回调函数)

(六)闭包

1、概念

闭包(closure)指有权访问另一个函数作用域中变量的函数

简单的理解就是,一个作用域可以访问另外一个函数内部的局部变量。

2、作用

闭包主要用来封装私有变量, 提供一些暴露的接口(让函数外部可以访问函数内部局部变量)

function fn() {
      var num = 10;
      return function () {
        console.log(num);
      }
    }
    var f = fn();
    f();

3、实际运用

// 1、利用动态添加属性实现for循环下的事件监听
    var lis = document.querySelector('.nav').querySelectorAll('li');
    for (var i=0; i<lis.length; i++) {
      lis[i].index = i;
      lis[i].onclick = function () {
        console.log(this.index);
      }
    }

    // 2、利用闭包实现上述功能
    for (var i=0; i<lis.length; i++) {
      // 利用for循环创建了4个立即执行函数,
      (function (i) {   //接收i
        lis[i].onclick = function () {
          console.log(i);
        }
      })(i)   // 把函数外部作用域(for里的)i传递进入立即执行函数内
    }

(七)递归函数

  • 函数内部自己调用自己,这个函数就是递归函数
  • 递归里面必须加退出条件(return)

(八)浅拷贝 & 深拷贝

1、浅拷贝概念

浅拷贝只拷贝一层,更深层次对象级别的只拷贝引用

var obj = {
      id: 1,
      name: 'andy',
      msg: {
        age: 18
      }
    };
    var o = {};
    for (var k in obj) {
      // k是属性名, obj[k]是属性值
      o[k] = obj[k];
    }
    console.log(o);
    o.msg = 20;
    console.log(o);
    console.log(obj);

上述代码中,由于obj内部还有一个对象msg,因此当for循环进行拷贝时,o拷贝到了obj最外面一层的属性,而更深层的对象则会被o拷贝走地址,所有当改变o.msg时,obj下面的msg属性的值也会随着发生改变。(第一层拷贝,后面的引用,就不拷贝了)

 2、浅拷贝语法糖

Object.assign(target, ...sources)        es6新增方法可以浅拷贝

        eg:        Object.assign(o, obj);

3、深拷贝概念

深拷贝拷贝多层,每一级别的数据都会拷贝

每一层都拷贝(利用递归)

var obj = {
      id: 1,
      name: 'andy',
      msg: {
        age: 18
      },
      color: ['black', 'white']
    };
    var o = {};
    // 封装函数
    function deepCopy(newobj, oldobj) {
      for (var k in oldobj) {
        // 判断属性值属于哪种类型
        // 1、获取属性值 oldobj[k]
        var item = oldobj[k];
        // 2、判断这个值是否是数组
        if (item instanceof Array) {
          newobj[k] = [];
          deepCopy(newobj[k], item);
        }
        // 3、判断这个值是否是对象
        else if (item instanceof Object) {
          newobj[k] = {};
          deepCopy(newobj[k], item);
        }
        // 4、属于简单数据类型
        else {
          newobj[k] = item;
        }
      }
    }
    deepCopy(o, obj);
    console.log(o);

二、原型与原型链

(一)函数的prototype 

1、函数的prototype属性

        每个函数都有一个prototype属性,它默认指向一个Object空对象(即称为:原型对象)//找爹

        原型对象中有一个属性constructor, 它指向(构造)函数对象        //找儿子

2、给原型对象添加属性(一般都是方法)

        作用:函数的所有实例对象自动拥有原型中的属性(方法)

(二)显示原型与隐式原型

1、每个函数function都有一个prototype,即显示原型(属性) 

        function Fn(){}                Fn.prototype

2、每个实例对象都有一个__proto__,可称为隐式原型(属性)

        var fn = new Fn()           fn.__proto__

3、实例对象的隐式原型的值等于对应构造函数的显示原型的值

        Fn.prototype = fn.__proto__

4、内存结构

function Fn() {   //内部语句:this.prototype = {}

    }
    console.log(Fn.prototype);

    var fn = new Fn();  //内部语句:this.__proto__ = Fn.prototype
    console.log(fn.__proto__);

    console.log(Fn.prototype === fn.__proto__); //true
    // 给原型添加方法
    Fn.prototype.test = function () {
      console.log('test()');
    }
    fn.test()

5、总结:

        函数的prototype属性:在定义函数时自动添加的,默认值是一个空Object对象

        对象的__proto__属性:创建对象时自动添加的,默认值为构造函数的prototype属性值

        程序员能直接操作显示原型,但不能直接操作隐式原型(ES6之前)

(三)原型链

        当访问对象的一个属性或方法时,它会先在自身中寻找,如果没有,向上去父级原型对象中寻找,再没有去祖父级,最多可向上寻找两级,即寻找到祖父级,最终没找到会返回undefined。

        别名:隐式原型链

        作用:查找对象的属性(方法) 

(四)探索instanceof

用途:判断左边的对象是不是右边对象的实例(前者是否属于后者)

        表达式:A instanceof B

        如果B函数的显示原型对象在A对象的原型链上,返回true,否则返回false

(五)原型对象的this指向问题

在构造函数和原型对象中,里面的this指向的都是对象实例

(六)原型对象的应用

扩展内置对象法(为构造函数的原型对象添加方法)

console.log(Array.prototype);
    Array.prototype.sum = function () {
      var sum = 0;
      for (var i=0; i<this.length; i++) {
        sum += this[i];
      }
      return sum;
    }
    var arr = [1, 2, 3];
    console.log(arr.sum());

三、继承

ES6之前并没有给我们提供extends继承。我们可以通过构造函数+原型对象模拟实现继承,被称为组合继承。

1、call()

调用这个函数,并且修改函数运行时的this指向。

        fun.call(thisArg, arg1, agr2, ...)

                thisArg: 当前调用函数this的指向对象

                agr:需要传递的参数

function fn(x, y) {
      console.log('hello world');
      console.log(this);
      console.log(x+y);
    }
    var o = {
      name: 'Bob'
    };
    // 1、调用函数:fn和fn.call效果相等
    fn();
    console.log('------------')
    fn.call();
    console.log('------------')
    // 2、改变函数的this指向:将o作为参数传进call即将fn的this指向改为了o,同时还能传递参数
    fn.call(o, 3, 4)

2、借用构造函数继承父类型属性

核心原理:通过call()把父类型的this指向子类型的this,这样就可以实现子类型继承父类型的属性。

// 借用父构造函数继承属性
    // 父构造函数
    function Father(uname, age) {
      this.uname = uname;
      this.age = age;
    }
    // 子构造函数
    function Son(uname, age) {
      Father.call(this, uname, age)   // 先调用父构造函数,然后再通过call将父构造函数中的this改为子构造函数的this,从而实现继承
    }
    var son = new Son("小明", 18);
    console.log(son);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值