JavaScript高级第1天 (下)函数进阶:定义函数的三种方式、函数的原型链结构、完整原型链、作用域以及作用域链、函数的四种调用模式、递归函数

函数进阶

定义函数的三种方式

函数声明

fn();//函数声明可以先调用,在声明
function fn(){
  console.log("这是函数声明")
}

函数表达式

var fn = function() {
  console.log("这是函数表达式");  
}
fn();//函数表达式必须先声明,再调用

构造函数Function

//函数也是对象,可以使用Function构造函数new出来
//相当于var fn = function(){}
var fn = new Function();

//语法:new Function(arg1,arg2,arg3..,body);
// 1. 所有的参数都是字符串类型。
// 2. 前面可以定义任意多个形参,最后一个参数是代码体。
var fn = new Function("alert(1111)");
fn();

var fn1 = new Function("a1", "a2", "alert(a1+a2)");
fn1(1,2);

函数也是对象

函数是由new Function创建出来的,因此函数也是一个对象, 所有的函数都是new Function的实例

函数的原型链结构

画出下列代码的原型链结构

//var Person = new Function();
function Person(){
  
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oBW1Qp8s-1594805197864)(images/function.jpg)]

Function.prototype常用成员

  • call:调用函数,重新指向this
  • apply:调用函数,重新指向this
  • bind:重新指向this,返回一个新的函数,不调用。

完整版原型链

绘制完整版原型链的目的是辅助大家理解js中对象的继承关系。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UdacRDD7-1594805197868)(./images/full.png)]

总结:

  1. 所有函数都是new Function创建出来的,因此所有函数.__proto__都是Function.prototype
  2. 所有对象都是new Object创建出来的,因此所有对象.__proto__都是Object.prototyp

测试1:

Function.prototype === Function.prototype   true
Object.__proto__ === Function.prototype       true
Function.prototype.__proto__ === Object.prototype  true
Object.prototype.__proto__ === Object.prototype    false
Object.__proto__.__proto__ === Object.prototype    true

测试2:

function Person() {}
var p = new Person()

console.log(p.constructor)
console.log(Person.constructor)

作用域

作用域:变量起作用的区域,也就是说:变量定义后,可以在哪个范围内使用该变量。

var num = 11;//全局变量
function fn(){
  var num1 = 22;//局部变量
  console.log(num);  // 全局变量在任何地方都能访问到
  console.log(num1);  
}
console.log(num);

在js里只有全局作用域和函数作用域。

函数作用域是在函数定义的时候作用域就确定下来了,和函数在哪调用无关。

var num = 123;
function f1() {
  console.log(num);
}

function f2(){
  var num = 456;
  f1();
}
f2();//打印啥?

作用域链

作用域链:只要是函数,就会形成一个作用域,如果这个函数被嵌套在其他函数中,那么外部函数也有自己的作用域,这个一直往上到全局环境,就形成了一个作用域链。

变量的搜索原则

  1. 从当前作用域开始查找是否声明了该变量,如果存在,那么就直接返回这个变量的值。
  2. 如果不存在,就会往上一层作用域查询,如果存在,就返回。
  3. 如果不存在,一直查询到全局作用域,如果存在,就返回。如果在全局中也没有找到该变量会报错

作用域链练习

// 1 
var num = 10;
fn1();
function fn1() {
  console.log(num);  // ?
  var num = 20;
  console.log(num);  // ?
}
console.log(num);    // ?


// 2 -- 改造上面的面试题
var num = 10;
fn1();
function fn1() {
  console.log(num);  // ?
  num = 20;
  console.log(num);  // ?
}
console.log(num);    // ?


// 3
var num = 123
function f1(num) {
    console.log(num) // ?
}

function f2() {
    var num = 456
    f1(num)
}
f2()


// 4
var num1 = 10;
var num2 = 20;
function fn(num1) {
  num1 = 100;
  num2 = 200;
  num3 = 300;
  console.log(num1);
  console.log(num2);
  console.log(num3);
  var num3;
}
fn();
console.log(num1);
console.log(num2);
console.log(num3);

函数的四种调用模式

根据函数内部this的指向不同,可以将函数的调用模式分成4种

  1. 函数调用模式
  2. 方法调用模式
  3. 构造函数调用模式
  4. 上下文调用模式(借用方法模式)
函数:当一个函数不是一个对象的属性时,我们称之为函数。
方法:当一个函数被保存为对象的一个属性时,我们称之为方法。

函数调用模式

如果一个函数不是一个对象的属性时,就是被当做一个函数来进行调用的。此时this指向了window

function fn(){
  console.log(this);//指向window
}
fn();

方法调用模式

当一个函数被保存为对象的一个属性时,我们称之为一个方法。当一个方法被调用时,this被绑定到当前对象

var obj = {
  sayHi:function(){
    console.log(this);//在方法调用模式中,this指向调用当前方法的对象。
  }
}
obj.sayHi();

构造函数调用模式

如果函数是通过new关键字进行调用的,此时this被绑定到创建出来的新对象上。

function Person(){
  console.log(this);
}
Person();//this指向什么?
var p = new Person();//this指向什么?

总结:分析this的问题,主要就是区分函数的调用模式,看函数是怎么被调用的。

  • 猜猜看:
// 分析思路:1. 看this是哪个函数的  2. 看这个函数是怎么调用的,处于什么调用模式

// 1
var age = 38;
var obj = {
    age: 18,
    getAge: function () {
        console.log(this.age);
    }
}

var f = obj.getAge;
f();//???


// 2
var age = 38;
var obj = {
  age:18,
  getAge:function () {
    console.log(this.age);//???
    function foo(){
      console.log(this.age);//????
    }
    foo();
  }
}
obj.getAge();
obj["getAge"]();


// 3
var length = 10

function fn() {
    console.log(this.length)
}
var obj = {
    length: 5,
    method: function (fn) {
        fn() 
        arguments[0]();
    }
}
obj.method(fn, 10, 5);

几种特殊的this指向

  • 定时器中的this指向了window,因为定时器的function最终是由window来调用的。
  • 事件中的this指向的是当前的元素,在事件触发的时候,浏览器让当前元素调用了function

上下文调用模式

上下文调用模式也叫方法借用模式,分为apply与call

使用方法: 函数.call() 或者 函数.apply()

call方法

call方法可以调用一个函数,并且可以指定这个函数的this指向

//所有的函数都可以使用call进行调用
//参数1:指定函数的this,如果不传,则this指向window
//其余参数:和函数的参数列表一模一样。
//说白了,call方法也可以和()一样,进行函数调用,call方法的第一个参数可以指定函数内部的this指向。
fn.call(thisArg, arg1, arg2, arg2);
  • 借用对象的方法
伪数组与数组

伪数组也叫类数组

  1. 伪数组其实就是一个对象,但是跟数组一样,伪数组也会有length属性,也有0,1,2,3等属性。
  2. 伪数组并没有数组的方法,不能使用push/pop等方法
  3. 伪数组可以跟数组一样进行遍历,通过下标操作。
  4. 常见的伪数组:argumentsdocument.getElementsByTagName的返回值jQuery对象
var arrayLike = {
  0:"张三",
  1:"李四",
  2:"王五",
  length:3
}
//伪数组可以和数组一样进行遍历
  • 伪数组借用数组的方法
Array.prototype.push.call(arrLike, "赵六");
apply方法

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

call和apply的使用场景:

  • 如果参数比较少,使用call会更加简洁
  • 如果参数存放在数组中,此时需要使用apply

课后练习:

1. 求数组的最大值和最小值
bind方法

**bind()**方法创建一个新的函数, 可以绑定新的函数的this指向

// 返回值:新的函数
// 参数:新函数的this指向,当绑定了新函数的this指向后,无论使用何种调用模式,this都不会改变。
var newFn = fn.bind(window);

递归函数

递归函数:函数内部直接或者间接的调用自己

递归的要求:

  1. 自己调用自己(直接或者间接)
  2. 要有结束条件(出口)

递归函数主要是化归思想 ,将一个复杂的问题简单化,主要用于解决数学中的一些问题居多。

  • 把要解决的问题,归结为已经解决的问题上。
  • 一定要考虑什么时候结束让函数结束,也就是停止递归(一定要有已知条件)

练习:

  • 计算1-100之间所有数的和
  • 深拷贝和浅拷贝
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值