一、前言
大家先预计一下以下四个函数调用的结果吧!
console.log('hello world')
return 'fsjohnhuang'
}
test.call() // ①
Function.prototype.call(test) // ②
Function.prototype.call.call(test) // ③
Function.prototype.call.call(Function.prototype.call, test) // ④
揭晓:①、③和④. 控制台显示hello world,并返回fsjohnhuang。②. 返回undefined且不会调用test函数;
那到底是啥回事呢?下面将一一道来。
二、从常用的call函数说起
还是通过代码说事吧
console.log(this)
return 'fsjohnhuang'
}
test2() // 控制台显示window对象信息,返回值为fsjohnhuang
test2.call({msg: 'hello world'}) // 控制台显示{msg:'hello world'}对象信息,返回值为fsjohnhuang
test2.call实际上是调用 Function.prototype.call (thisArg [ , arg1 [ , arg2, … ] ] ) ,而其作用我想大家都了解的,但其内部的工作原理是怎样的呢? 这时我们可以参考ECMAScript5.1语言规范。以下是参照规范的伪代码(各浏览器的具体实现均不尽相同)
/*** 注意:this指向调用call的那个对象或函数 ***/
// 1. 调用内部的IsCallable(this)检查是否可调用,返回false则抛TypeError
if (![[IsCallable]](this)) throw new TypeError()
// 2. 创建一个空列表
// 3. 将arg1及后面的入参保存到argList中
var argList = [].slice.call(arguments, 1)
// 4. 调用内部的[[Call]]函数
return [[Call]](this, thisArg, argList)
}
那现在我们可以分析一下 ①test.call() ,并以其为基础去理解后续的内容。它内部实现的伪代码如下:
if (![[IsCallable]](test)) throw new TypeError()
var argList = [].slice.call(arguments, 1)
return [[Call]](test, thisArg, argList)
}
下面我们再来分析② Function.prototype.call(test) ,伪代码如下:
/*** Function.prototype是一个function Empty(){}函数 ***/
if (![[IsCallable]](Function.prototype)) throw new TypeError()
var argList = [].slice.call(arguments, 1)
// 实际上就是调用Empty函数而已,那返回undefined是理所当然的
return [[Call]](Function.prototype, test, argList)
}
三、Function.prototype.call.call内部究竟又干嘛了?
有了上面的基础那么Function.prototype.call.call就不难理解了。就是以最后一个call函数的thisArg作为Function.prototype.call的this值啦!伪代码如下:
Function.prototype.call.call = function(test, arg1, arg2,...){
if ([[IsCallable]](Function.prototype.call)) throw new TypeError()
var argList = [].slice.call(arguments, 1)
return [[Call]](Function.prototype.call, test, argList)
}
// test作为函数的this值
// 注意:入参thisArg的值为Function.prototype.call.call的入参arg1
Function.prototype.call = function(thisArg, arg1, arg2,...){
if ([[IsCallable]](test)) throw new TypeError()
var argList = [].slice.call(arguments, 1)
return [[Call]](test, thisArg, argList)
}
四、见鬼的合体技——Function.prototype.call.call(Function.prototype.call, test)
看伪代码理解吧!
Function.prototype.call.call = function(Function.prototype.call, test){
if ([[IsCallable]](Function.prototype.call)) throw new TypeError()
var argList = [].slice.call(arguments, 1)
return [[Call]](Function.prototype.call, Function.prototype.call, argList)
}
Function.prototype.call = function(test){
if ([[IsCallable]](Function.prototype.call)) throw new TypeError()
var argList = [].slice.call(arguments, 1)
return [[Call]](Function.prototype.call, test, argList)
}
Function.prototype.call = function(thisArg){
if ([[IsCallable]](test)) throw程序猿的简约之道 - xingoo 阅读原文»
前言:
这几天经过不断的洗脑与学习,作为一个码农也算是对产品的设计有了一点点的想法。这里就忍不住的记录一下,仅仅抒发一下自己澎湃的心情。
最近看了本书《简约至上》,书还没看完,就在Infoq上看到了《猎豹移动的工程师文化》这一视频。他们都有一个共性,就是简约。
简约至上奉承一切的产品设计都要足够简单,符合用户的使用习惯,过多的设计可能导致产品易用性的下降。而猎豹的清理大师国际版也是如此,从一开始设计到最后上线都是四个按钮,不是因为做不出其他的功能,而是把最主要的功能展现给用户,让界面看起来不那么杂乱。
有些人可能会觉得我作为一个后台程序员或者服务器工程师,或者是网络安全专家,无需了解什么产品设计,其实不然。你设计的每一个类,每一个方法,都应该足够的简单。不是说功能的单一,而是通过精心的设计隐藏掉复杂的实现,展现给用户或者使用者的是一个最简单的接口或者外观。
首先说简约:
少即是多这个道理,在视频中进行了阐述,以iphone为例,仅仅一个按钮就实现了很多复杂的功能。像以前的诺基亚全键盘的手机,做一些功能的设计通过键盘敲击就可以实现,而现在一个按钮就要做到很多复杂的事情是需要花费更多的时间和精力来设计,但是展现给用户的却是简单的一个按钮。
再谈用户需求
无论是做产品还是项目,即便是做科研,最终的目的都是为了使用。落不了地的技术都是空谈,而为了落地使用就需要先了解需求。
软件业外除了自己做产品做项目自己使用外,其他的基本都是需求者与产品开发者分离,比如外包或者专们做软件的公司。这样就很容易导致需求者与设计者与开发者的想法不一致,最终导致最终开发出来的产品,设计人员受不了,使用者更是诧异!这是神马?
传统的瀑布模型软件工程,通过详细的阶段设计也许不会造成太大的诧异。但是现在的公司估计很少有按照这种标准走下来的,奉行敏捷开发,各种迭代周期,与用户或者设计者沟通欠缺,最终导致产品不断的重新设计开发。
究其原因,就是因为开发者总是按照自己的想法来设计软件。
我认为用户可能想要这个!我认为用户操作的时候应该这样!我认为用户必须需要这个功能!
总结起来,都是开发者自己认为!这也不是软件开发人员的错,毕竟我们也只是想做一个完美的产品。
功能的强大带来的是易用性的忽略。
因此,首先了解用户的业务需求,了解问题的本质!再设想如果我遇到这个问题,我想要怎么解决。再去设计软件开发,效果也就不同了。
基于领域驱动的模型设计也是如此,以业务导向驱动开发,最终会给用户带来一个业务上的软件,而不是复杂功能拼凑的集合体。
重视用户的需求,再去设计软件,并且考虑到易用性以及操作习惯,才是开发者需要注意的地方。
简约四策略
书中也为我们提供了几种简约的方法:删除、组织、隐藏、转移。
删除:
即要删除掉一些不重要的功能,这些功能的存在也许是开发者为了考虑周到而设计。但是使用者根本不会去使用的功能,这些功能的存在简直就是浪费开发者的生命,也是浪费公司维护的成本。如果一个软件有很多复杂的功能,面向用户群也很多,那么可以综合的考虑,几种公用的重要功能进行详细的设计,而一些锦上添花的功能就随意处理了。
组织:
一个网站或者APP如果界面杂乱,用户进去都不知道做什么,那就是设计开发的失败了。设计者应该把一个类型或者一个业务领域的功能组织起来,进行分类,这样不同的操作场景只需要使用固定的区域,不会造成使用的混乱。
隐藏:
有时候一些不必要的功能隐藏掉,在必要的时候,可以通过复杂的操作展现给用户也是可以的。毕竟这些操作都是极少数情况下才会使用的。比如微信界面从一开始到现在都没怎么变过,但是增添了很多复杂的功能都是隐藏在主页下面的,可能你点击+,才会看到这些功能。如果每一次上线的新工呢过都往主页上放,那么这个产品也许主页往下拉一天也拉不完了。
转移:
简单的说,就是一些功能的转移。比如余额宝或者当面付,后台需要很多复杂的校验以及计算。但是需要用户操作的就是简单的密码确认而已,这种功能由前端转移到后台也是一种设计的转移。应该让用户尽可能少的去操作,但是关键的动作还需要用户感觉到有控制感!而不是被软件控制。
这里也推荐一下蚂蚁金服的程立做的演讲,收获很多!他在视频中说过,我们尽可能少的让用户去做一些事情,因此用户根本不应该去关心这些。
功能的转移就像动物的迁徙,从一个地方转移到另一个地方,不是删除或者不要这个功能,而是转移到别处进行处理。
最后单一与极致
这句话取自猎豹的工程师文化视频,单一与极致,意味着功能的简约,但却做到极致。要不不做,要不就做到极致。这是一种做事的态度吧!
就像我们写软件,就应该写出来具有可用性与可扩展性的代码。可用性意味着,不能写完一段代码,要不就全是BUG。缺乏可扩展性意味着,有了类似的应用场景,却发现根本无法二次使用。
如果遇到这种情况,也推荐不要重新再去写一段新的代码,要重构!重构不是说产品上线了,或者项目开发结束了,再回去把代码架构翻新一边,而是开发的过程中,看到两段相似的代码,就提取出来进行整合(这也是infoq上面学到的)。当然,前一阵看一个博友进行代码的重构,结果代码量少了很多,有些公司按照代码量考量一些程序猿的工作量,这个......此处省略一万字。
最后呢,感谢公司知识网站让我接触到产品设计的书籍,也感谢infoq,让我收获良多。
本文链接:程序猿的简约之道,转载请注明。
没有评论:
发表评论