cocos学习-this的指向

在学习cocos点击事件的时候,产生了这样一个疑问:

this.node.on(cc.Node.EventType.MOUSE_UP, this.onMouseUp,this)

 这个方法里面的参数,第一个是代表监听鼠标抬起时候,第二个是调用的方法名,那第三个是什么呢?之前一直是this.node这样用的。

    start () {
        this.node.on(cc.Node.EventType.MOUSE_UP, this.onMouseUp,this)
    }
    onMouseUp(){
        cc.log(this); 
    }

我尝试输出这个this,得到这样的结果

ClickTest是我挂载在node上的脚本,this代表了这个脚本,所以之前想要拿到自己定义或者挂载的东西的时候就可以用this.XXX

在查阅资料后,发现要改变这个this的指向,就要接触到bind、apply、call这三个东西

call

我将脚本挂载在一个node上面,点击node就会调用一次call:

const {ccclass, property} = cc._decorator;
var tmp = {
    name:'我的名字'
};
@ccclass
export default class ClickTest extends cc.Component {
    start () {
    //启动鼠标监听
        this.node.on(cc.Node.EventType.MOUSE_UP, this.onMouseUp,this)
    }

    private person(){
        cc.log(this.name); 
    }

    onMouseUp(){
        this.person.call(tmp);
    }
    //输出‘我的名字’
}
const {ccclass, property} = cc._decorator;
var tmp = {
    name:'我的名字',
    person:function(){
        cc.log(this.name); 
    }
};
@ccclass
export default class ClickTest extends cc.Component {
    start () {
        this.node.on(cc.Node.EventType.MOUSE_UP, this.onMouseUp,this)
    }
    // private person(){
    //     cc.log(this.name); 
    // }
    onMouseUp(){
        tmp.person();
    }
}

两次输出的结果都是  ‘我的名字’

这样用到的是this的隐式绑定,当函数person引用有上下文对象的时候,就会把函数person调用中的this,绑定到这个上下文对象tmp了。

简单来说,第二次能够输出‘我的名字’是因为tmp里面的person函数中出现this,this就会自动绑定到tmp上面。因为tmp直接执行了person函数,tmp是真正的调用位置,因此this直接指向了tmp

而第一种调用call输入的‘我的名字’是因为call的第一个参数代表了this的指向,call可以改变this的指向

为了更好的理解call的原理,我想模仿call写一个newCall:

因为call在构造函数的原型对象里面

要模仿写一个call就必须在原型对象里面添加新的和call一样的属性,尝试用不同的方式输出this:

var tmp = {
    name:'我的名字',    
};
function person(){
    cc.log(this);
}
Function.prototype.newCall = function(obj){
    cc.log(this)  
}
@ccclass
export default class ClickTest extends cc.Component {
    start () {
        this.node.on(cc.Node.EventType.MOUSE_UP, this.onMouseUp,this)
    }
    onMouseUp(){
        person();
        cc.log(this)
        person.newCall(tmp);
        person.call(tmp)
    }
}

    结果如下:

1.person()  因为没有在类里面调用到它所有输出默认的window
2.cc.log(this)  是在类中调用的,所以this指向类,输出ClickTest
3.person.newCall(tmp)  person直接执行了newCall函数,person是真正的调用位置,所以输出了person

4.person执行call之后,this要指向tmp,所以输出tmp对象。

那如何让newCall可以像call一样找到tmp呢?

既然原来的newCall里面this指向了person,但是没有执行person,那我们创建一个对象p,把person赋给p,p可以执行person不就好了

var tmp = {
    name:'我的名字',    
};
function person(){
    cc.log(this.name);
}
Function.prototype.newCall = function(obj){
    //cc.log(this)  
    obj.p = this;
    obj.p();
    delete obj.p;
}
@ccclass
export default class ClickTest extends cc.Component {

    start () {
        this.node.on(cc.Node.EventType.MOUSE_UP, this.onMouseUp,this)
    }
    onMouseUp(){
        person.call(tmp);
        person.newCall(tmp);
    }
}

输出结果:

call的第一个参数是this的指向,后面还可以接受很多个参数,在newCall里面,可以把参数存在arguments内,然后用eval()函数将传入的字符串作为 JavaScript 代码进行执行:

var tmp = {
    name:'我的名字',    
};

function person(a,b,c,d){
    cc.log(this.name);
    cc.log(a,b,c,d);
}

Function.prototype.newCall = function(obj){
    //cc.log(this)  
    obj.p = this;
    var newArguments = [];
    for(var i=1;i<arguments.length;i++){
        newArguments.push('arguments[' + i + ']');
    }
    eval('obj.p('+newArguments+')');
    delete obj.p;
}

@ccclass
export default class ClickTest extends cc.Component {

    start () {
        this.node.on(cc.Node.EventType.MOUSE_UP, this.onMouseUp,this)
    }
    onMouseUp(){
        //person.apply(tmp);
        person.newCall(tmp,'1a','2b','3c','4d');
    }
}

结果: 

apply

apply其实和call没有太大的区别,不同之处在于提供参数的方式。apply 使用参数数组而不是一组参数列表。apply 可以使用数组字面量(array literal),如 fun.apply(this, [‘eat’, ‘bananas’]),或数组对象, 如 fun.apply(this, new Array(‘eat’, ‘bananas’))。

所以写newApply的时候,就要传入第二个形参作为数组:

var tmp = {
    name:'我的名字',    
};

function person(a,b,c,d){
    cc.log(this.name);
    cc.log(a,b,c,d);
}

Function.prototype.newApply = function(obj,arr){
    //cc.log(this)  
    obj.p = this;
    if(!arr){
        obj.p();
    }else{
        var newArguments = [];
        for(var i=0;i<arr.length;i++){
            newArguments.push('arr[' + i + ']');
        }
        eval('obj.p('+newArguments+')');
    }
    delete obj.p;
}

@ccclass
export default class ClickTest extends cc.Component {
    start () {
        this.node.on(cc.Node.EventType.MOUSE_UP, this.onMouseUp,this)
    }
    onMouseUp(){
        //person.apply(tmp);
        person.newApply(tmp,['1a','2b','3c','4d']);
    }
}

结果:

Bind:

bind会返回一个函数,可以像call一样后面跟着很多参数

所以写newBind的话需要return function(){},而且要注意把传入的arguments对象的第一个数组元素切割,但是对象不能直接用数组的切割方法,需要利用call方法把slice切割方法赋给arguments对象,如下:

var tmp = {
    name:'我的名字',    
};

function person(){
    cc.log(this.name);
}

Function.prototype.newBind = function(obj){
    //this指向的是person函数
    var that = this,
    arr = Array.prototype.slice.call(arguments,1);
    cc.log(arr);
    return function(){
        //里面其实就是实现call和apply的功能 要接收一个数组显然很麻烦,因此使用apply,在控制参数方面,还是apply会比较好用
        that.apply(obj);
    }
}

@ccclass
export default class ClickTest extends cc.Component {
    start () {
        this.node.on(cc.Node.EventType.MOUSE_UP, this.onMouseUp,this)
    }
    onMouseUp(){
        //person.apply(tmp);
        person.newBind(tmp,'1a','2a')();
    }
}

那bind的第二个括号里面应该放的是什么呢?

在newBind里面其实有两个不同的arguments对象,把它们输出出来:

const {ccclass, property} = cc._decorator;

var tmp = {
    name:'我的名字',    
};

function person(test){
    cc.log(this.name);
    cc.log(test);
    
}

Function.prototype.newBind = function(obj){
    //第一个
    cc.log(arguments)
    return function(){
    //第二个
        cc.log(arguments)
    }
}

@ccclass
export default class ClickTest extends cc.Component {
    start () {
        this.node.on(cc.Node.EventType.MOUSE_UP, this.onMouseUp,this)
    }
    onMouseUp(){
        person.newBind(tmp,'1a','2a')('3c');
    }
}

结果:

第一个arguments里面包含第一个括号里面的参数

第二个arguments里面包含第二个括号里面的参数

那就只需要进行切割然后用concat合并就好了:

var tmp = {
    name:'我的名字',    
};

function person(a,b,c){
    cc.log(this.name);
    cc.log(a,b,c);
}

Function.prototype.newBind = function(obj){
    var that = this,
    //获取第一个括号内的参数
    arr = Array.prototype.slice.call(arguments,1);
    return function(){
        //arr2获取第二个括号里面的
        var arr2 = Array.prototype.slice.call(arguments),
        // arrSum合并两个括号里面的参数(除了第一个参数外)
            arrSum = arr.concat(arr2);
        that.apply(obj,arrSum);
    }
}

@ccclass
export default class ClickTest extends cc.Component {
    start () {
        this.node.on(cc.Node.EventType.MOUSE_UP, this.onMouseUp,this)
    }
    onMouseUp(){
        person.newBind(tmp,'1a','2a')('3c');
    }
}

结果:

  • 16
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值