1.谈谈变量提升
把当前上下文中带有var(变量)/function(函数)进行提升的声明或者定义。变量提升是将变量声明提升到它所在作用域的最开始的部分。
实例一
function a() {
console.log(b);
var b = 'b变量';
}
a();//undefined;
// 这里是因为把函数里面的变量t提升到函数最开始的部分,如下:
function a(){
var b;
console.log(b)
b='b变量'
}
a()
function(函数)声明会比var(变量)声明优先级更高一点。
console.log(a) //函数体
var a = '变量a'
console.log(a) //变量a
function a(){
console.log('函数a')
}
console.log(a) // 变量a
//上面的代码把变量/函数提到最开始的地方,注意函数的优先级比变量高,如下:
function a(){
console.log('函数a')
}
var a ;
console.log(a) //函数体
a='变量a'
console.log(a) // 变量a
console.log(a) // 变量a
js在生成执行环境时,会有两个阶段 。
1.是创建的阶段, JS 解释器会找出需要提升的变量和函数, 并且给他们 提前在内存中开辟好空间, 函数的话会将整个函数存⼊内存中, 变量只声明并且赋值为 undefined ,
2.代码执行阶段, 我们可以 直接提前使用声明的变量或函数
- 在提升的过程中,相同的函数会覆盖上⼀个函数,如下
console.log(a) // 函数A2的函数体
function a(){
console.log('函数A1')
}
function a(){
console.log('函数A2')
}
因为var命令会发生“变量提升”现象,所以ES6中引入了let,它所声明的变量一定要在声明后使用,否则报错
// var 的情况
console.log(foo); // 输出undefined
var foo = 2;
// let 的情况
console.log(bar); // 报错ReferenceError
let bar = 2;
2. bind 、call 、apply 区别
call
和apply
都是为了解决改变this
的指向 。作用都是相同的, 只是传参的方式
不同。- 除了第⼀个参数外,
call
可以接收⼀个参数列表,apply
只接受⼀个参数数组
let a = {
value:1
}
function getValue(name,age){
console.log(name) // 小土豆
console.log(age) // 18
console.log(this.value) // undefined
}
getValue('小土豆',18)
// 上面例子中,this.value为undefined 我们想访问a中的value,就用call,apply如下:
let a = {
value:'call'
}
let b = {
value:'aplly'
}
function getValue(name,age){
console.log(name) // 小土豆call 小土豆aplly
console.log(age) // 9 10
console.log(this.value) //call applly
}
getValue.call(a,'小土豆call',9) //可以接收⼀个参数列表
getValue.apply(b,['小土豆aplly',10]) // 只接受⼀个参数数组
bind
和其他两个方法作用也是⼀致的, 只是该方法会返回⼀个函数 。并且我
们可以通过bind
实现柯里化
let a = {
value: 'bind'
}
function getValue(name, age) {
console.log(name) // 小土豆bind
console.log(age) // 28
console.log(this.value) //bind
}
let whatbind = getValue.bind(a, '小土豆bind', 28)
// bind返回的是一个函数
whatbind()
3.如何实现⼀个bind,call ,apply 函数
对于实现bind函数, 可以从⼏个方面思考
1. 不传⼊第⼀个参数,那么默认为window
2. 改变了 this 指向,让新的对象可以执行该函数 。那么思路是否可以变成给新的对象添加
⼀个函数,然后在执行完以后删除?
实现bind函数
// 2.定义我们的myBind
Function.prototype.myBind = function (context) {
// 3.检查this
// 这里的函数为getValue调用所以this指向getValue函数
if (typeof this !== 'function') {
throw new TypeError('Error')
}
var _this = this
// 4.删除第一个参数 a
let args = [...arguments].slice(1)
// 5.bind函数会返回一个函数 所以我们这儿也返回一个函数
return function A() {
// 因为这里return了个函数所以 这里的函数为 whatbind所调用 this指向 Window
// 判断
if (this instanceof A) {
return new _this(...args, ...arguments)
}
// 把getValue的this指向第一个参数 同时跟参数return出去
return _this.apply(context, args.concat(...arguments))
}
}
// 1假设我们写好了自己的bind函数命名为myBind 跟 bind的用法保持一致
let a = {
value: 'bind函数'
}
function getValue(name, age) {
console.log(name);
console.log(age);
console.log(this.value);
}
let whatbind = getValue.myBind(a, '我是bind', '20min')
// 6.定义的bind返回函数所以我们得调用
whatbind(
实现call函数
Function.prototype.myCall = function (context) {
var context = context || window
// 给 context 添加⼀个属性
// getValue.call(a, '我是call', '20min') => a.fn = getValue
context.fn = this
// 将 context 后面的参数取出来
var args = [...arguments].slice(1)
// getValue.call(a, '我是call', '20min') => a.fn('我是call', '20min')
var result = context.fn(...args)
//删除 fn
delete context.fn
return result
}
let a = {
value: "call函数"
}
function getValue(name, age) {
console.log(name);
console.log(age);
console.log(this.value);
}
getValue.myCall(a, '我是call', '20min')
实现apply函数
Function.prototype.myApply = function (context) {
var context = context || window
context.fn = this
var result
// 需要判断是否存在第二个参数
// 如果存在,就将第二个参数展开
if (arguments[1]) {
result = context.fn(...arguments[1])
} else {
result = context.fn()
}
delete context.fn
return result
}
let a = {
value: "apply函数"
}
function getValue(name, age) {
console.log(name);
console.log(age);
console.log(this.value);
}
getValue.myApply(a, ['我是apply', '20min'])