挂载在页面和组件的this区别
挂载在page的this
Page({
something: "挂载在page的this变量",
onLoad() {
console.log("page this内容", this);
this.handleOther();
},
handleOther() {},
});
something和handleOther都有,并且原型proto也存了一份。
挂载在component的this
Component({
something: "挂载在component的this变量",
lifetimes: {
attached() {
this.componentTest = "componentTest";
this.handleOther();
console.log("component this内容", this);
},
},
methods: {
handleOther() {},
},
});
只有componentTest变量,没有something变量。且方法存在了原型_proto_上。
影响:如果认为跟UI渲染相关的数据存储在this.data对象上,而不需要用来渲染的数据,不放在this.data对象的开发者来说(这样某种程度可以加快渲染,减轻this.data的体量)。如果是把数据存储在this对象上,需要区分是page还是compenent:
- 如果是page可以直接Page({xx: yy}),这时候可以通过this.xx来获取;
- 如果是component,不能直接Component({xx:yy}),需要在页面初始化的时候,手动绑定在this对象上,如attached() {this.xx = yy;}
小程序Component组件在闭包函数内获取不到正确的this
父组件wxml
<testComponent value="{{1}}" />
<testComponent value="{{2}}" />
<testComponent value="{{3}}" />
获取不到this的情况
Component({
properties: {
value: {
type: Number,
value: 0,
},
},
lifetimes: {
attached() {
this.handleTest(this.data.value);
},
},
methods: {
handleTest: (function () {
let instance = undefined;
return function (value) {
if (!instance) {
instance = function (value) {
console.log("test", value, this);
};
}
instance(value);
};
})(),
},
});
获取到同一个this的情况
Component({
properties: {
value: {
type: Number,
value: 0,
},
},
lifetimes: {
attached() {
this.handleTest(this.data.value);
},
},
methods: {
handleTest: (function () {
let instance = undefined;
return function (value) {
if (!instance) {
instance = (value) => { // 箭头函数创建时确定this指向
console.log("test", value, this, this.data.value);
};
}
instance(value);
};
})(),
},
});
方案1
运行时执行闭包函数,在函数外部传递this值给内部函数。
缺点:通过传值的方式,欠缺优雅。
Component({
properties: {
value: {
type: Number,
value: 0,
},
},
lifetimes: {
attached() {
this.handleTest()(this.data.value); // 注意点
},
},
methods: {
handleTest: function () {
let instance = undefined;
const that = this; // 注意点
return function (value) {
if (!instance) {
instance = function (value) {
console.log("test", value, that); // 注意点
};
}
instance(value);
};
},
},
});
方案2
闭包函数执行时,调用call方法传递this。
缺点:通过传值的方式,欠缺优雅。
Component({
properties: {
id: {
type: Number,
value: 0,
},
},
lifetimes: {
attached() {
this.handleTest(this.id);
}
},
methods: {
handleTest: (function () {
let instance = undefined;
return function (id) {
if (!instance) {
instance = function (id) { // 注意点
console.log("test", id, this);
};
}
instance.call(this, id); // 注意点
};
})(),
},
});
方案3
把instance变量定义成全局的,全局能拿到this。
缺点:instance实际是handleTest函数的业务(内部变量),只会在handleTest函数里面用,没必要定义成全局变量。
Component({
properties: {
value: {
type: Number,
value: 0,
},
},
lifetimes: {
attached() {
this.instance = undefined; // 注意点
this.handleTest(this.data.value);
}
},
methods: {
handleTest: (function () { // 这里也可以不用再包一层函数
return function (value) {
if (!this.instance) {
this.instance = function (value) { // 注意点
console.log("test", value, this);
};
}
this.instance(value); // 注意点
};
})(),
},
});
结合防抖节流函数的几种测试案例
可以获取到变化的this,但无节流效果(每执行一次handleTest函数都打印一次日志,非3秒内只打印一次):
handleTest: (() => {
let instance = undefined;
return function (value) {
if (!this.instance) {
instance = ThrottleUtil.throttle(3000, (value) => {
console.log('test', this);
});
}
instance(value);
};
})(),
通过传递this参数,有节流效果,但this获取不到。
handleTest: (() => {
return ThrottleUtil.throttle(3000, (value, that) => { // 此时的value是小程序action回调函数的evt参数,并不是this.data.value值
console.log('test', value, that);
});
})(),
通过传递this参数,有节流效果,但this获取不到。
handleTest: ThrottleUtil.throttle(3000, (value, that) => { // 此时的value是小程序action回调函数的evt参数,并不是this.data.value值
console.log('test', value, that);
}),
不传递this参数,闭包,有节流效果,this也能正确获取。
handleTest: (function () {
let instance = undefined;
if (!instance) {
instance = ThrottleUtil.throttle(
3000,
function (value) {
console.log('this', value, this, this.data.value); // 第一个参数value是小程序action回调函数的evt参数,并不是this.data.value值
}
);
}
return instance;
})(),
export default class ThrottleUtil {
/**
* 节流
* 每隔一段时间,只执行一次函数。
* @param delay 判定时间
* @param action 实际方法
* @param options 选项对象:
* {
* toggleLastArg // boolean。在delay时间内多次触发的话,是否选择最后一次触发的参数来执行,默认为false(只触发第一次点击)
* }
* @returns {Function} 回调,回调实际的方法
*/
static throttle = (delay, action = () => {}, options) => { // 箭头函数
let last = 0;
const toggleLastArg = !!(options && options.toggleLastArg);
let toggleLastArgTimer = null;
// eslint-disable-next-line func-names
return function (...args) { // 匿名函数
if (toggleLastArgTimer) clearTimeout(toggleLastArgTimer);
const curr = new Date().getTime();
if (curr - last > delay) {
action.call(this, ...args); // 用call把this绑定在回调函数上
last = curr;
} else if (toggleLastArg) {
toggleLastArgTimer = setTimeout(() => {
action.call(this, ...args);
}, delay);
}
};
}
/**
* 防抖
* 定义:多次触发事件后,事件处理函数只执行一次,并且是在触发操作结束时执行
* 原理:对处理函数进行延时操作,若设定的延时到来之前,再次触发事件,则清除上一次的延时操作定时器,重新定时。
* @param delay 判定时间
* @param action 实际方法
* @returns {Function} 回调,回调实际的方法
*/
static debounce = (delay, action = () => {}) => {
let timer = null;
// eslint-disable-next-line func-names
return function (...args) {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
action.call(this, ...args);
}, delay);
};
}
}
参考资料
bind/call/apply区别:
三个函数存在的区别, 用一句话来说的话就是: bind是返回对应函数(每一次返回一个新函数), 便于稍后调用; apply, call则是立即调用. 除此外, 在 ES6 的箭头函数下, call 和 apply 的this绑定失效,即就算手动call\apply绑定this,也不会改变它的指向。
箭头函数的this:
- 函数体内的 this 对象, 就是定义时所在的对象, 而不是使用时所在的对象;
- 不可以当作构造函数, 也就是说不可以使用 new 命令, 否则会抛出一个错误;
- 不可以使用 arguments 对象, 该对象在函数体内不存在. 如果要用, 可以用 Rest 参数代替;
- 不可以使用 yield 命令, 因此箭头函数不能用作 Generator 函数;