Day19:THIS和函数进阶

本文详细介绍了JavaScript中原型、this指向、构造函数、原型链的概念及其应用,包括如何在原型对象上添加方法,对象的原型查找机制,以及构造函数实例和原型对象的关系。还讨论了闭包和改变this指向的方法如call()。

1.原型和this指向

原型对象prototype

概念

是一个对象,每一个构造函数里面都有一个prototype属性,指向这个对象本身

prototype一般称为原型对象

作用

可以把不变的方法直接定义到原型对象上(prototype),所有对象的实例,都可以共享这个方法

避免在创建对象时为对象的方法重复开辟空间

位置

写在构造函数上(也可以认为是类上),通过打印构造函数可以看到

对象的原型proto

概念

每一个对象都有一个属性__proto__,指向构造函数的原型对象(prototype)

作用

为对象查找方法提供了一个机制(先从构造函数中找,如果找不到,再去它指定的原型对象中找

位置

在对象上,通过打印对象,就可以看到

// 验证对象的原型是否指向原型对象
console.log(ldh.__proto__ === Star.prototype) // true
// 原型对象(在构造函数上),本质是类的一个静态成员(属性)
console.dir(Star)
console.dir(Star.prototype) // object

// 对象的原型(在对象上),本质是对象的一个属性
console.dir(ldh)
console.dir(ldh.__proto__)

// 对象的原型指向原型对象
console.log(ldh.__proto__ === Star.prototype) // true

/* 对象的原型应用场景
为对象查找方法提供了一个机制(先从构造函数中找,如果找不到,再去它指定的原型对象中找 */
构造函数constructor

概念

不管是原型对象还是对象原型,里面都有一个属性constructor

constructor一般称之为构造函数(用来创建对象)

作用

主要用于创建对象,还可以记录该对象引用于哪个构造函数

位置

存在于原型对象(prototype)或者对象原型(__proto__)的属性中

// 原型对象,它的constructor属性就指向构造函数
console.dir(Star.prototype)

// 对象的原型,它的constructor属性就指向构造函数
console.dir(ldh.__proto__)

// 打印这两个对象中的构造函数
console.log(Star.prototype.constructor)
console.log(ldh.__proto__.constructor)

原型链

概念

每一个实例对象都有一个__proto__属性,指向构造函数的原型对象,原型对象也是一个对象,也有__proto__属性,它指向上一层的原型对象,这样一层层向上找,就形成了原型链

父类的原型为,原型对象的对象的原型

当前类的原型就是__proto__

通过对象的原型找到原型对象

// 通过对象的原型找到原型对象(对象的原型指向原型对象)
console.log(ldh.__proto__)
// 原型对象也是一个对象,也有__proto__这个属性,打印的其实是原型对象的原型,其实就是它的上一层
console.log(ldh.__proto__.__proto__) // object
// 继续往上找
console.log(ldh.__proto__.__proto__.__proto__) // object已经是最顶层,上面妹有了

作用

当实例对象调用方法的时候,首先会在构造函数定义的方法中去查找。

如果没有,就通过对象的__proto__属性找到原型对象,看原型对象里有没有定义。

如果原型对象中也没有,就通过原型对象的__proto__属性找到它的上一级,看有无对应方法

这样一层层找,一直找到object,如果都没有,就报错;哪一层有,就用哪一层

/* 给js的Array类,扩展一个求数字类型数组内部元素和的方法(sum方法)
模拟js官方的做法,将sum方法定义在Array的原型上面,将来的Array对象都可以调用  */
// 拿了原型.sum 名字  = 方法
Array.prototype.sum = function(){
  var sum = this.reduce(function(total,num){
    return total + num
  },0)
  return sum
}

// 使用方法
var arr = [1,2,3]
var res = arr.sum()
console.log(res)
构造函数实例和原型对象三角关系

1.构造函数的prototype属性指向了构造函数原型对象

2.实例对象是由构造函数创建的,实例对象的__proto__属性指向了构造函数的原型对象

3.构造函数的原型对象的constructor属性指向了构造函数,实例对象的原型的constructor属性也指向了构造函数

原型对象中的this指向
function Star(uname, age) {
    this.uname = uname;
    this.age = age;
}
var that;
Star.prototype.sing = function() {
    console.log('我会唱歌');
    that = this;
}
var ldh = new Star('刘德华', 18);
// 1. 在构造函数中,里面this指向的是对象实例 ldh
console.log(that === ldh);//true
// 2.原型对象函数里面的this 指向的是 实例对象 ldh

2.函数进阶

函数调用及this指向总结

函数类型

命名函数

匿名函数

构造函数

字面量对象中定义的方法

事件处理函数

定时器里面的函数

立即执行函数

格式:

(function(形式参数){

函数体

})(实际参数)

<script>
    // 命名函数
    function f1() {
        console.log('命名函数中的this')
        console.log(this) // window
    }
    f1()
    // 匿名函数
    var f2 = function () {
        console.log('匿名函数中的this')
        console.log(this) // window
    }
    f2()
    // 构造函数
    function Student() {
        console.log('构造函数中的this')
        console.log(this) // 创建的那个对象
    }
    var stu = new Student()
    // 字面量对象中定义的方法
    var obj = {
        name: '张三',
        eat: function () {
            console.log('字面量对象中的this')
            console.log(this) // 调用方法的那个字面量对象
        }
    }
    obj.eat()
    // 事件处理函数
    document.querySelector('button').addEventListener('click', function () {
        console.log('事件处理函数中的this')
        console.log(this) // 触发事件的事件源对象
    })
    // 定时器里面的函数
    setTimeout(function () {
        console.log('定时器函数中的this')
        console.log(this) // window
    }, 2000); // 需要加分号,否则会影响下面的立即执行函数
    // 立即执行函数
    (function () {
        console.log('立即执行函数中的this')
        console.log(this) // window
    })()
</script>

函数类型

调用方式总结

this指向总结

命名函数

函数名

window

匿名函数

变量名

window

构造函数

new

创建出的实例对象

字面量对象中定义的方法

字面量对象

调用方法的字面量对象

事件处理函数

事件触发时自动调用

事件源对象

定时器里面的函数

设置时间到了自动调用

window

立即执行函数

定义完立即调用

window

改变this指向

call方法

格式:方法名.call(方法调用者,参数列表)

var res2 = getMax.call(window,20,30)

作用

可以调用函数

可以改变函数内的this指向

高阶函数

函数也是一个类型,也可以作为参数和返回值。像这样的函数,叫做高阶函数

例如:Array中的很多方法,find() filter()

闭包

也是一个函数,有权访问另外一个函数作用域内的变量,这个变量所在的函数就是闭包

f2函数有权访问f1函数作用域的num变量,num所在的函数f1就是闭包

通过断点调试,进入f2的内部,可以看到f1是一个闭包

通过断点,可以看到函数内部也形成了一个闭包(f1函数),并且f1调用完成后,num还在内层,没有被销毁

// 使用定时器打印里面的元素的内容
var lis = document.querySelectorAll('li')

// 遍历打印
for (var i = 0; i < lis.length; i++) {
  // console.log(lis[i].innerHTML)
  // 使用定时器打印
  /* setTimeout(function(){
  console.log(lis[i].innerHTML)
  },1000) */
  // 使用立即执行函数解决,只要拿到一个i,立刻执行一次函数
  // 就不会造成for循环执行结束才打印的i超值问题
  (function (index) {
    setTimeout(function () {
      console.log(lis[index].innerHTML)
    }, 1000)
  })(i)
}

出错的原因:因为定时器里面的代码需要一秒钟后执行,而for循环一开始就执行完了

执行的时候会不断地给i赋值,

i的值就从0变成了3,定时器里面的函数,在一秒钟之后才能执行。此时lis[i]已经自加变成了3

没有3这个元素,就会报错

解决

可以使用立即执行函数,每遍历一次,就拿到i的值,立即执行定时器

使用let获取li就会报错

通过断点,可以看到立即执行函数里面形成了一个小闭包

立即执行函数里面的匿名函数就是外层函数,定时器里面的函数就是内层函数

内层函数访问外层函数里面的变量index

这样外层函数就形成了一个小闭包

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值