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
这样外层函数就形成了一个小闭包