在学习拉勾朱德龙老师课程做的笔记,要学具体的链接在这里
https://kaiwu.lagou.com/course/courseInfo.htm?courseId=180#/detail/pc?id=3179
数据类型和函数是编程重要概念,前者储存数据,后者储存代码
在JavaScript中函数比较强大,用途比较多
可以赋值给一个变量
可以有自己的属性
可以作为参数被传递
this 关键字
this是JavaScript一个关键字,是一个指针,指向调用它的的对象
首先this指向的是一个对象,是函数执行上下文的对象
其次这个对象指向的是调用它的对象
如果调用它的不是对象或者对象不存在,则指向全局对象(严格模式下是undefined)
例子
当代码 1 执行 fun() 函数时,实际上就是通过对象 obj 来调用的,所以 this 指向对象 obj。
代码 2 也是同样的道理,通过实例 O 来调用,this 指向类实例 O。
代码 3 则可以看成是通过全局对象来调用,this 会指向全局对象(需要注意的是,严格模式下会是 undefined)。
//代码1
var obj = {
fun(){
console.log(this)
}
}
obj.fun()
// {fun: ƒ} obj对象自己
//代码2
class O {
fun(){
console.log(this)
}
}
var o = new O()
o.fun()
// 打印出 O
//代码3
function foo(){
console.log(this)
}
foo()
//window
换个例子
(1)函数fn1 里面嵌套函数 fn2,里面函数打印this 指向
function fn1(){
console.log(this)
}
function fn2(){
fn1()
}
fn2()
//window
this在这里没有找到调用它的对象所以指向全局对象window
(2)嵌套函数被定义到对象
function fn() {console.log(this)}
function fn2() {fn()}
var obj = {fn2}
obj.fn2()
这里虽然fn2被放在了对象里然后执行的,但是调用fn的仍然是fn2 不是对象obj 所以this指向还是window
虽然 fn2() 作为 obj 的属性调用,但 fn2()中的 this 指向并不会传递给函数 fn()
(3)对象里有属性为数组,数组forEach循环 this指向
var obj = {
arr:[1,2,3]
}
obj.arr.forEach(function (){
console.log(this)
})
//undefined
看过 forEach 的说明文档便会知道,forEach接受两个参数 第一个是回调函数,第二个是指向指针,没有传默认为undefined
所以这里this指向undefined
类似的,需要传入 this 指向的函数还有:every()、find()、findIndex()、map()、some(),在使用的时候需要特别注意。
(4)创建一个类,new一个 该类的构造函数的实例,然后实例属性的函数 赋值给一个变量,该变量调用这个方法
class B{
fun(){
console.log(this)
}
}
var b = new B()
var fn = b.fun
fn()
//undefined
因为该变量还是全局变量,所以this指向的还是全局,但是这里有一个隐藏知识点,es6 里面class类是严格模式
所以最后的this指向 是undefined
(5)ES6的箭头函数不会创建自己的this,他只会从自己的作用域链上一层继承this,理解为箭头函数的 this 继承自上层的 this,但在全局环境下定义仍会指向全局对象
var arrow = {fn: () => {
console.log(this)
}}
arrow.fn()
//window
(7)this指向一个基础类型,基础类型会转换为引用对象,所以这里 this 指向的是一个值为 0 的 Number 类型对象
[0].forEach(function() {console.log(this)}, 0) // Number {0}
(8)改变this指向的常见三种方式 apply,call,bind
改变 this 指向的常见 3 种方式有 bind、call 和 apply。call 和 apply 用法功能基本类似,都是通过传入 this 指向的对象以及参数来调用函数。
区别在于传参方式,前者为逐个参数传递,后者将参数放入一个数组,以数组的形式传递。bind 有些特殊,
它不但可以绑定 this 指向也可以绑定函数参数并返回一个新的函数,当 c 调用新的函数时,绑定之后的 this 或参数将无法再被改变。
var getname = function(){
console.log(this.name)
}
var b= getname.bind({name:'bind'})
b() //bind
var a = getname.apply({name:'apply'})
a() //apply
var c = getname.apply({name:'call'})
c() //call
由于 this 指向的不确定性,所以很容易在调用时发生意想不到的情况。在编写代码时,应尽量避免使用 this,
比如可以写成纯函数的形式,也可以通过参数来传递上下文对象。实在要使用 this 的话,
可以考虑使用 bind 等方式将其绑定。
补充 1:箭头函数
箭头函数和普通函数相比,有以下几个区别,在开发中应特别注意:
- 不绑定arguments对象,箭头函数内访问arguments对象会报错
- 不能用作构造器,也就是不能new 它构建的实例
- 默认不会创建prototype原型属性
- 不能用作 Generator() 函数,不能使用 yeild 关键字。
函数的转换
编写一个 add() 函数,支持对多个参数求和以及多次调用求和。示例如下:
add(1) // 1
add(1)(2)// 3
add(1, 2)(3, 4, 5)(6) // 21
要实现这个要求,我们必须知道函数相关的两个隐式转换函数 toString() 和 valueOf()。
toString() 函数会在打印函数的时候调用,比如 console.log、valueOf 会在获取函数原始值时调用,比如加法操作。
具体代码实现如下,在 add() 函数内部定义一个 fn() 函数并返回。
fn() 函数的主要职能就是拼接参数并返回自身,
当调用 toString() 和 valueOf() 函数时对拼接好的参数进行累加求和并返回。
function add(...args) {
let arr = args
function fn(...newArgs) {
arr = [...arr, ...newArgs]
return fn;
}
fn.toString = fn.valueOf = function() {
return arr.reduce((acc, cur) => acc + parseInt(cur))
}
return fn
}