自从学会call/apply/bind这三个方法后我就各种场合各种使用各种得心应手至今还没踩过什么坑,怎么用?说直白点就是我自己的对象没有某个方法但别人有,我就可以通过call/apply/bind去调用执行别人家的方法,不太懂具体用法的同学可移至MDN学习一下Function.prototype.call() Function.prototype.apply() Function.prototype.bind() ,本文不讲解使用,但是这三个方法并不是万能的,并不一定会执行你想要的那个函数,因为可能要看函数中上下文对象的某些属性特性值允许不允许改变,今天这个坑我就深深踩了一下,并探索了一番。之所以说并不一定是因为确实有部分类数组可以传入call/apply/bind且成功调用,比如arguments(虽然它是[object Arguments]类型的,但__proto__却指向Object.prototype),感兴趣的可以试试。

事件起因
在学习HTML5与类相关的扩充其中classList属性时候,我了解到所有元素有classList这个属性,并且这个属性是新集合DOMTokenList的实例,在DOMTokenList.prototype上有一个add(value)方法,用来将给定字符串值添加到DOMTokenList实例列表中,并且即时反应到文档页面。如果在列表中value值已存在,就不添加了。我手贱寻思着自己用JS代码把这个add方法大概实现一下(肯定没人家JS引擎实现的好,我就实现个思路),但就这一实现才发现的这个坑。
(1).先来看这个add方法JS引擎给它设置的属性特性值如何,是可写的:

(2).然后实现我们的add函数

Object.defineProperty(DOMTokenList.prototype, 'add', {
configureable : true,
enumerable : true,
writable : true,
value : function(value){
if([].indexOf.call(this, value)>=0) return;
//加到classList类数组中
[].push.call(this, value);
}
});

是不是感觉没什么错,我就是想把'classA'加入到DOMTokenList实例列表中,然而当测试后控制台会报错

分析探讨
这个报错内容为"类型错误:不能设置改变[object Object]的那个特性值只有getter的length属性"。意思就是[object Object]就是这里的DOMTokenList.prototype,你换成childNodes它也同样会给你提示出错。

这里push方法因为会改变数组的长度length,而且不止push,pop,unshift,shift的执行也会改变数组长度,经测试它们会报出同样的错。这里的DOMTokenList.prototype.length获取它的特性值为:

length的访问器属性中set特性被引擎设置为undefiend了,怪不得不能将value加到document.body.classList类数组中,因为引擎没有给你提供set的接口函数啊,只给你提供get特性函数返回length长度。而我们平常使用的这个不会报错是因为length属性的value值是可变的,注意这里其实是每个数组实例都有自己的length属性。

不过好在DOMTokenList.prototype的length属性configurable特性是true,意味着我可以自己写length的set函数。现在整个过程就是:当实现我自己的add函数,因为add函数中调用到push操作,当执行push操作时会更新DOMTokenList.prototype.length(自动调用set函数),我可以在set函数中执行相关处理,先拿console测试一下是不是这个流程

Object.defineProperty(DOMTokenList.prototype, 'length', {
set : function(){console.log('执行了')}
})


确实是的,而且现在不报错了。但是我现在做的只是皮毛,'classA'还没有加到document.body.classList中去呢,看看列表里还只是"document":

不知道push函数引擎是怎么实现的,这说明JS引擎好像就没执行push函数,此方法行不通...不清楚它为什么不执行push操作??

那还是乖乖转化为数组再处理吧。常见的是将类数组转化为真正的数组再做相关处理,方法挺多比如Array.from(),Array.prototype.slice.call()等等,不过这会改变原来类数组的类型啊,我就想问怎么样处理能给类数组添加项而不改变类数组的类型。数组实例的类型由它的__proto__决定,那就好办了,不过得先设置classList属性的一些特性项,JS引擎给的set是undefiend

最后重写下来为:

Object.defineProperty(DOMTokenList.prototype, 'add', {
configureable : true,
enumerable : true,
writable : true,
value : function(value){
if([].indexOf.call(this, value)>=0) return;
//加到classList类数组中
var newarr = Array.from(this);
newarr.push(value);
newarr.__proto__ = DOMTokenList.prototype;
document.body.classList = newarr;
}
}); Object.defineProperty(Element.prototype, 'classList', {
set: function(value){
console.log(value);
//这里怎么处理不同元素的classList值是JS引擎的事,我实在是不会了
}
})

不过这样写有点固定具体元素了继续重写为:

Object.defineProperty(DOMTokenList.prototype, 'add', {
configureable : true,
enumerable : true,
writable : true,
value : function(value, ele){
if([].indexOf.call(this, value)>=0) return;
//加到classList类数组中
var newarr = Array.from(this);
newarr.push(value);
newarr.__proto__ = DOMTokenList.prototype;
ele.classList = newarr;
}
}); Object.defineProperty(Element.prototype, 'classList', {
set: function(value){
console.log(value);
//这里怎么处理不同元素的classList值是JS引擎的事,我实在是不会了
}
}); //使用
document.body.classList.add('classA', document.body);//["classA"]

总结
受限于JS引擎中元素实例的某些属性是共享于其原型属性的属性值的set函数,虽然最后也没有全部实现add函数的功能,我是实在不知道JS引擎中怎么实现的set,get函数从而保证不同实例共享原型上同一属性而且还保证不同实例的该属性值不一样,这估计得看引擎源码了,心累...有知道的同学可以说一下思路吗??还有对于我上面的分析部分我有两点疑问:知道的大神求科普!!
(1).JS引擎好像就没执行push函数,此方法行不通...不清楚它为什么不执行push操作
(2).怎么样处理能给类数组添加项而不改变类数组的类型

别真以为JavaScript中func.call/apply/bind是万能的!的更多相关文章

  1. 深入浅出:了解JavaScript中的call,apply,bind的差别

     在 javascript之 this 关键字详解文章中,谈及了如下内容,做一个简单的回顾:         1.this对象的涵义就是指向当前对象中的属性和方法.       2.this指向的可变 ...

  2. javascript中的call(),apply(),bind()方法的区别

    之前一直迷惑,记不住call(),apply(),bind()的区别.不知道如何使用,一直处于懵懂的状态.直到有一天面试被问到了这三个方法的区别,所以觉得很有必要总结一下. 如果有不全面的地方,后续再 ...

  3. Javascript中call,apply,bind的区别

    一.探索call方法原理 Function.prototype.call = function(obj) { // 1.让fn中的this指向obj // eval(this.toString().r ...

  4. JavaScript中call、apply个人理解

    JavaScript中call.apply个人理解 一句话即通俗的说:call.apply 是为了改变this的状态而存在的 }; } function personInfo(name,age){ t ...

  5. 说说 JavaScript中 call和apply

    下面有关JavaScript中 call和apply的描述,错误的是? call与apply都属于Function.prototype的一个方法,所以每个function实例都有call.apply属 ...

  6. js中的call,apply,bind区别

    在JavaScript中,call.apply和bind是Function对象自带的三个方法,这三个方法的主要作用是改变函数中的this指向. call.apply.bind方法的共同点和区别:app ...

  7. JavaScript学习(2)call&apply&bind&eval用法

    javascript学习(2)call&apply&bind&eval用法 在javascript中存在这样几种特别有用的函数,能方便我们实现各种奇技淫巧.其中,call.bi ...

  8. Learning JavaScript with MDN (call, apply, bind)

    Learning JavaScript with MDN (call, apply, bind) call, apply, bind Object.prototype.toString() 检测 js ...

  9. Javascript中call、apply、bind函数

    javascript在函数创建的时候除了自己定义的参数外还会自动新增this和arguments两个参数 javascript中函数也是对象,call.apply.bind函数就是函数中的三个函数,这 ...

随机推荐

  1. Servlet开发-----基础及MVC设计模式

    一.Servlet介绍   Servlet本身只是普通的Java类,只有当容器为他创建了ServletConfig和ServletContext时才成为了一个Servlet:   Servlet简单的 ...

  2. 【noip模拟题】最大公约数(数论)

    好神的一题... 首先我们只需要枚举这个gcd即可..从大到小,然后问题转换为判定问题...即判定是否有k个数有gcd这个约数.. orz 这样做的复杂度最坏是O(n+n/2+n/3+…+n/n)=O ...

  3. Hibernate_day04--HQL多表查询_Hibernate检索策略

    HQL多表查询 Mysql里面多表查询 1 内连接 查的是两个表关联的数据 2 左外连接 查的是左边表所有数据,右边表关联数据 3 右外连接 HQL实现多表查询 Hql多表查询 (1)内连接 (2)左 ...

  4. 自学Ajax

    使用Ajax快捷函数 说明 出于简化AJAX开发工作的流程,jQuery提供了若干了快捷函数. 实例 1.显示 test.php 返回值(HTML 或 XML,取决于返回值). $.get(" ...

  5. java基础---->Java中异常的使用(一)

    今天我们大致学习一下java中关于异常的知识.原来忍住一段时间不联系一个人,真的就不想联系了. java异常的使用 一.java异常的一些说明 .Throwable 类是Java 语言中所有错误或异常 ...

  6. python反序列化研究学习

    零.补充: 补充于2018-02-08,之前研究时候有一个疑惑,python的序列化成二进制,打web服务怎么传这个二进制对象呢,今天请教了身边大神(传说的九零后黑客代表),可以使用base64传输. ...

  7. 【BZOJ2905】背单词 fail树+DFS序+线段树

    [BZOJ2905]背单词 Description 给定一张包含N个单词的表,每个单词有个价值W.要求从中选出一个子序列使得其中的每个单词是后一个单词的子串,最大化子序列中W的和. Input 第一行 ...

  8. 『SharePoint 2010』Sharepoint 2010 Form 身份认证的实现(基于SQL)

    1:创建一个基于身份认证的应用程序(具体参见上篇基于AD) SQL-MembershipProvider 成员SQL-RoleManager 角色 2:修改管理中心,我们创建的应用程序,还有Web服务 ...

  9. Linux安装vsftpd

    卸载vsftpd sudo yum remove vsftpd 安装vsftpd sudo yum -y install vsftpd 创建一个文件夹用来当作ftp得仓库 cd / sudo mkdi ...

  10. 第十课——cluster故障转移操作,codis部署

    作业描述] 1.cluster的故障转移操作,截图展示 2.部署codis,并写代码访问codis ================================================== ...