Js的E到底加不加Q伤害.微博莫名关注了很多人人说有BuG是加

谢邀。。。&br&&br&&br&&br&我们写界面的时候,经常会碰到一些功能,比如,从某些数据动态生成界面,然后从输入控件采集数据,经过变换,更新另外的界面。&br&&br&整个这个过程,其实归纳来看,就两件事:&br&&br&- 根据数据的变动,生成或者变更界面&br&- 根据界面的操作,变更数据&br&&br&所有的界面相关操作流程,都可以化为若干这两种操作的组合。写多了之后,我们就可以总结出,两者又都是包含一些模式的,比如说,数据的变动,它包含:&br&&br&- 简单类型的创建赋值&br&- 引用类型的创建和赋值&br&- 数组元素的添加,移除,交换、赋值&br&- 对象属性的添加、删除、赋值&br&&br&界面的变更,包含:&br&&br&- 元素的创建、交换、移位、移除&br&- 属性的创建、赋值、移除&br&&br&再总结下去,就会发现,数据和界面的变更之间,一般都是存在对应关系的。&br&&br&比如说,一个数组跟界面一个列表对应,两者始终是同步的,一个变了,另外一个也要跟着变。一个变量或者对象的键值,对应到一个元素的属性上,一个变了,另外一个也要跟着变。&br&&br&既然这样,如果我们能够引入一种绑定关系,经过一系列的配置过程,使得以后每次数据发生变更,界面都会自动跟着作对应变动;界面上的操作,也会自动更新到数据,那开发过程就会非常省事了,绝大部分此类操作都会转化为配置,供绑定框架用来建立数据和界面之间的关联关系。&br&&br&所以,数据绑定是一个非常广泛而迫切的需求,任何一个现代界面开发体系,如果不提供某种数据绑定机制,其开发过程就必定是低效的。&br&&br&那么,界面,很好理解,肯定是View,数据,如果说是Model,那ViewModel又是什么?&br&&br&以购物车结算页面为例,包含的内容有:&br&&br&- 已选商品列表(每种商品可以添加或者移除数量,并且有每种商品的总价)&br&- 商品总数和总价&br&&br&从View的角度,一般不会有太多疑问,看到的东西,可以视业务场景做分块,每块单独实现,但是Model呢?&br&&br&我们知道Model是一种数据,但并非每个数据都属于Model,一般来说,在页面上的数据中,用于跟服务端交互的数据,可以大致视为Model,回到我们这个例子中看,那就是每种商品的已选数量。&br&&br&但另外还有一些东西是要用于展示或者中间步骤的,那就是每种商品的总价,还有商品总数、总价,这些东西由于不提交,仅供视图使用,所以可以视为ViewModel。&br&&br&ViewModel是Model和View之间的桥梁,它的设计原则是:&br&&br&- 为Model和View提供适配&br&- 如果有需要转换的过程,尽可能在ViewModel中做,保持Model的纯洁,View的清晰。&br&&br&我们上面这个例子中,数据的转换并不明显。大家都见过行政区划树吧,省市县乡村,可能Model给的数据都是平级,然后根据展示的需要,转换成树状的,转换之后的数据,就是专供展示用的ViewModel了。&br&&br&综上,MVVM所包含的Model,View,ViewModel三层,在实践的时候,主要包含几个要点:&br&&br&- 以不同的角色分别考虑每个层次&br&- 先考虑Model和View,最后考虑ViewModel&br&&br&这个ViewModel,责任可不轻,就像中年男人,是婆媳关系的纽带、桥梁,要想两头满意,就得自己多干,脏活累活全都我来,你们歇着……&br&&br&--------------------&br&欠两个例子代码,闲了用Vue补个
谢邀。。。 我们写界面的时候,经常会碰到一些功能,比如,从某些数据动态生成界面,然后从输入控件采集数据,经过变换,更新另外的界面。 整个这个过程,其实归纳来看,就两件事: - 根据数据的变动,生成或者变更界面 - 根据界面的操作,变更数据 所有的…
JavaScript 闭包的本质源自两点,词法作用域和函数当作值传递。&br&&br&词法作用域,就是,按照代码书写时的样子,内部函数可以访问函数外面的变量。引擎通过数据结构和算法表示一个函数,使得在代码解释执行时按照词法作用域的规则,可以访问外围的变量,这些变量就登记在相应的数据结构中。&br&&br&函数当作值传递,即所谓的first class对象。就是可以把函数当作一个值来赋值,当作参数传给别的函数,也可以把函数当作一个值 return。一个函数被当作值返回时,也就相当于返回了一个通道,这个通道可以访问这个函数词法作用域中的变量,即函数所需要的数据结构保存了下来,数据结构中的值在外层函数执行时创建,外层函数执行完毕时理因销毁,但由于内部函数作为值返回出去,这些值得以保存下来。而且无法直接访问,必须通过返回的函数。这也就是私有性。&br&&br&本来执行过程和词法作用域是封闭的,这种返回的函数就好比是一个虫洞,开了挂。也就是 &a href=&///people/c85b513fb617ceca494b& data-hash=&c85b513fb617ceca494b& class=&member_mention& data-tip=&p$b$c85b513fb617ceca494b& data-hovercard=&p$b$c85b513fb617ceca494b&&@秋月凉&/a& 答案中轮子哥那句话的意思。&br&&br&显然,闭包的形成很简单,在执行过程完毕后,返回函数,或者将函数得以保留下来,即形成闭包。实际上在 JavaScript 代码中闭包不要太常见。&br&&br&函数作为第一等对象之后 JavaScript 灵活得不要不要的。
JavaScript 闭包的本质源自两点,词法作用域和函数当作值传递。 词法作用域,就是,按照代码书写时的样子,内部函数可以访问函数外面的变量。引擎通过数据结构和算法表示一个函数,使得在代码解释执行时按照词法作用域的规则,可以访问外围的变量,这些变量…
题主先试试在Chrome的console里对比一下这四段代码:&br&&div class=&highlight&&&pre&&code class=&language-js&&&span class=&k&&for&/span& &span class=&p&&(&/span&&span class=&kd&&var&/span& &span class=&nx&&i&/span& &span class=&o&&=&/span& &span class=&mi&&1&/span&&span class=&p&&;&/span& &span class=&nx&&i&/span& &span class=&o&&&=&/span& &span class=&mi&&10&/span&&span class=&p&&;&/span& &span class=&nx&&i&/span&&span class=&o&&++&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&nx&&console&/span&&span class=&p&&.&/span&&span class=&nx&&time&/span&&span class=&p&&(&/span&&span class=&nx&&i&/span&&span class=&p&&);&/span&
&span class=&k&&for&/span& &span class=&p&&(&/span&&span class=&kd&&var&/span& &span class=&nx&&j&/span& &span class=&o&&=&/span& &span class=&mi&&0&/span&&span class=&p&&;&/span& &span class=&nx&&j&/span& &span class=&o&&&&/span&&span class=&mi&&256&/span&&span class=&o&&*&/span&&span class=&mi&&256&/span&&span class=&o&&*&/span&&span class=&mi&&256&/span&&span class=&p&&;&/span& &span class=&nx&&j&/span&&span class=&o&&++&/span&&span class=&p&&)&/span& &span class=&p&&{&/span& &span class=&p&&};&/span&
&span class=&nx&&console&/span&&span class=&p&&.&/span&&span class=&nx&&timeEnd&/span&&span class=&p&&(&/span&&span class=&nx&&i&/span&&span class=&p&&);&/span&
&span class=&p&&}&/span&
&span class=&cm&&/* or the version below&/span&
&span class=&cm&&
eval(&for (var i = 1; i &= 10; i++) {
&span class=&cm&&
console.time(i);
&span class=&cm&&
for (var j = 0; j &256*256*256; j++) { }; \&/span&
&span class=&cm&&
console.timeEnd(i);
&span class=&cm&&
}&)&/span&
&span class=&cm&&*/&/span&
&/code&&/pre&&/div&和:&br&&div class=&highlight&&&pre&&code class=&language-js&&&span class=&kd&&function&/span& &span class=&nx&&foo&/span&&span class=&p&&()&/span& &span class=&p&&{&/span&
&span class=&nb&&eval&/span&&span class=&p&&(&/span&&span class=&s2&&&for (var i = 1; i &= 10; i++) {
&span class=&s2&&
console.time(i);
&span class=&s2&&
for (var j = 0; j &256*256*256; j++) { }; \&/span&
&span class=&s2&&
console.timeEnd(i);
&span class=&s2&&
}&&/span&&span class=&p&&)&/span&
&span class=&p&&}&/span&
&span class=&nx&&foo&/span&&span class=&p&&()&/span&
&/code&&/pre&&/div&还有:&br&&div class=&highlight&&&pre&&code class=&language-js&&&span class=&kd&&function&/span& &span class=&nx&&foo&/span&&span class=&p&&()&/span& &span class=&p&&{&/span&
&span class=&p&&(&/span&&span class=&mi&&0&/span&&span class=&p&&,&/span&&span class=&nb&&eval&/span&&span class=&p&&)(&/span&&span class=&s2&&&for (var i = 1; i &= 10; i++) {
&span class=&s2&&
console.time(i);
&span class=&s2&&
for (var j = 0; j &256*256*256; j++) { }; \&/span&
&span class=&s2&&
console.timeEnd(i);
&span class=&s2&&
}&&/span&&span class=&p&&)&/span&
&span class=&p&&}&/span&
&span class=&nx&&foo&/span&&span class=&p&&()&/span&
&/code&&/pre&&/div&最后:&br&&div class=&highlight&&&pre&&code class=&language-js&&&span class=&kd&&function&/span& &span class=&nx&&foo&/span&&span class=&p&&()&/span& &span class=&p&&{&/span&
&span class=&k&&for&/span& &span class=&p&&(&/span&&span class=&kd&&var&/span& &span class=&nx&&i&/span& &span class=&o&&=&/span& &span class=&mi&&1&/span&&span class=&p&&;&/span& &span class=&nx&&i&/span& &span class=&o&&&=&/span& &span class=&mi&&10&/span&&span class=&p&&;&/span& &span class=&nx&&i&/span&&span class=&o&&++&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&nx&&console&/span&&span class=&p&&.&/span&&span class=&nx&&time&/span&&span class=&p&&(&/span&&span class=&nx&&i&/span&&span class=&p&&);&/span&
&span class=&k&&for&/span& &span class=&p&&(&/span&&span class=&kd&&var&/span& &span class=&nx&&j&/span& &span class=&o&&=&/span& &span class=&mi&&0&/span&&span class=&p&&;&/span& &span class=&nx&&j&/span& &span class=&o&&&&/span&&span class=&mi&&256&/span&&span class=&o&&*&/span&&span class=&mi&&256&/span&&span class=&o&&*&/span&&span class=&mi&&256&/span&&span class=&p&&;&/span& &span class=&nx&&j&/span&&span class=&o&&++&/span&&span class=&p&&)&/span& &span class=&p&&{&/span& &span class=&p&&};&/span&
&span class=&nx&&console&/span&&span class=&p&&.&/span&&span class=&nx&&timeEnd&/span&&span class=&p&&(&/span&&span class=&nx&&i&/span&&span class=&p&&);&/span&
&span class=&p&&}&/span&
&span class=&p&&}&/span&
&span class=&nx&&foo&/span&&span class=&p&&()&/span&
&/code&&/pre&&/div&有没有惊喜?&br&&br&在我的本上跑,&br&&ol&&li&大概ms&br&&/li&&li&大概ms&/li&&li&大概150-160ms&/li&&li&大概9-10ms&/li&&/ol&&br&恭喜题主中了eval大招…细节解释起来略麻烦。回头有空再说…
题主先试试在Chrome的console里对比一下这四段代码: for (var i = 1; i &= 10; i++) {
console.time(i);
for (var j = 0; j &256*256*256; j++) { };
console.timeEnd(i);
/* or the version below
eval("for (var i = 1; i &= 10; i++) { \
这个问题,我前不久刚在 Quora 答了个几乎一模一样的,不想再翻译一遍了,真想看就当学英文吧:&a href=&///?target=https%3A///Which-should-I-learn-Mithril-Vue-or-Angular/answer/Evan-You-3& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://www.&/span&&span class=&visible&&/Which-should-&/span&&span class=&invisible&&I-learn-Mithril-Vue-or-Angular/answer/Evan-You-3&/span&&span class=&ellipsis&&&/span&&i class=&icon-external&&&/i&&/a&&br&&br&概括一下就是,想要只学一个一劳永逸,那是不可能的。好好打基础,然后多尝试不同风格的框架,因为只有尝试过后才能理解比如
&a data-hash=&cc0145aee04dd53cc6590edd& href=&///people/cc0145aee04dd53cc6590edd& class=&member_mention& data-tip=&p$b$cc0145aee04dd53cc6590edd& data-hovercard=&p$b$cc0145aee04dd53cc6590edd&&@徐飞&/a& 提到的各种权衡,也只有尝试过后才能知道哪个能真正提升自己的开发效率。说没精力,那是借口。&br&&br&如果你不是专业前端,那就用 Vue 吧!哈哈哈
这个问题,我前不久刚在 Quora 答了个几乎一模一样的,不想再翻译一遍了,真想看就当学英文吧: 概括一下就是,想要只学一个一劳永逸,那是不可能的。好好打基础,然后多尝试不同风格的框架,因为只有尝试过后才能理解比如
少上知乎,少听牛逼,别过分追新,别过分厌旧,技术不过一碗饭,再喜欢在中国也是先是饭后是事业,饭吃稳了再是菜,锦上添花。多挣钱,活好就是为挣钱,没意义的为了学而学有毛前途?三两人引领新技术的事悠着点做,扯着蛋了反而不好。2016年没计划,能写代码写代码,写不了是我吃不了这口饭,就去干点别的
少上知乎,少听牛逼,别过分追新,别过分厌旧,技术不过一碗饭,再喜欢在中国也是先是饭后是事业,饭吃稳了再是菜,锦上添花。多挣钱,活好就是为挣钱,没意义的为了学而学有毛前途?三两人引领新技术的事悠着点做,扯着蛋了反而不好。2016年没计划,能写代…
正好这段时间在重新看这部分,写一篇回答来梳理一下吧。&br&&br&__proto__(隐式原型)与prototype(显式原型)&br&&br&1.&b&是什么&/b&&br&&ul&&li& 显式原型 explicit prototype property:&br&&/li&&/ul&每一个函数在创建之后都会拥有一个名为prototype的属性,这个属性指向函数的原型对象。&br&&b&Note&/b&:通过Function.prototype.bind方法构造出来的函数是个例外,它没有prototype属性。(感谢 &a data-hash=&56f3848afea094ea8f76a5& href=&///people/56f3848afea094ea8f76a5& class=&member_mention& data-tip=&p$b$56f3848afea094ea8f76a5& data-hovercard=&p$b$56f3848afea094ea8f76a5&&@陈禹鲁&/a& 同学的答案让我知道这一点)&br&&blockquote&NOTE Function objects created using Function.prototype.bind do not have a prototype property or the [[Code]], [[FormalParameters]], and [[Scope]] internal properties. ----- &a href=&///?target=http%3A//www.ecma-international.org/ecma-262/5.1/%23sec-15.3.4.5& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&ECMAScript Language Specification&i class=&icon-external&&&/i&&/a&&/blockquote&&ul&&li&隐式原型 implicit prototype link:&br&&/li&&/ul&JavaScript中任意对象都有一个内置属性[[prototype]],在ES5之前没有标准的方法访问这个内置属性,但是大多数浏览器都支持通过__proto__来访问。ES5中有了对于这个内置属性标准的Get方法Object.getPrototypeOf().
&br&Note: Object.prototype 这个对象是个例外,它的__proto__值为null &br&&ul&&li&二者的关系:&br&&/li&&/ul&隐式原型指向&b&创建&/b&这个对象的函数(constructor)的prototype&br&&br&&b&2. 作用是什么&/b&&br&&ul&&li& 显式原型的作用:用来实现基于原型的继承与属性的共享。&br&&/li&&/ul&&blockquote& ECMAScript does not use classes such as those in C++, Smalltalk, or Java. Instead objects may be created in various ways including via a literal notation or via constructors which create objects and then execute code that initialises all or part of them by assigning initial values to their properties. Each constructor is a function that has &b&a property named “prototype” that is used to implement prototype-based inheritance and shared properties&/b&.Objects are created by using constructor for example, new Date(2009,11) creates a new Date object. ----&a href=&///?target=http%3A//www.ecma-international.org/ecma-262/5.1/%23sec-4.2.1& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&ECMAScript Language Specification&i class=&icon-external&&&/i&&/a&&/blockquote&&ul&&li& 隐式原型的作用:构成原型链,同样用于实现基于原型的继承。举个例子,当我们访问obj这个对象中的x属性时,如果在obj中找不到,那么就会沿着__proto__依次查找。&br&&/li&&/ul&&blockquote&Every object created by a constructor has an implicit reference (called the object’s prototype) to the value of its constructor’s “prototype” ----&a href=&///?target=http%3A//www.ecma-international.org/ecma-262/5.1/%23sec-4.2.1& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&ECMAScript Language Specification&i class=&icon-external&&&/i&&/a&&/blockquote&&br&&b&3. __proto__的指向&/b&&br&__proto__的指向到底如何判断呢?根据ECMA定义 'to the value of its constructor’s &prototype& ' ----指向创建这个对象的函数的显式原型。所以关键的点在于找到创建这个对象的构造函数,接下来就来看一下JS中对象被创建的方式,一眼看过去似乎有三种方式:(1)对象字面量的方式 (2)new 的方式 (3)ES5中的Object.create() 但是我认为本质上只有一种方式,也就是通过new来创建。为什么这么说呢,首先字面量的方式是一种为了开发人员更方便创建对象的一个语法糖,本质就是 var o = new Object(); o.xx =o.yy= 再来看看Object.create(),这是ES5中新增的方法,在这之前这被称为原型式继承,&br&&blockquote&道格拉斯在2006年写了一篇文章,题为 Prototypal Inheritance In JavaScript。在这篇文章中,他介绍了一种实现继承的方法,这种方法并没有使用严格意义上的构造函数。他的想法是借助原型可以基于已有的对象创建新对象,同时还不比因此创建自定义类型,为了达到这个目的,他给出了如下函数:
&/blockquote&&div class=&highlight&&&pre&&code class=&language-js&&&span class=&kd&&function&/span& &span class=&nx&&object&/span&&span class=&p&&(&/span&&span class=&nx&&o&/span&&span class=&p&&){&/span&
&span class=&kd&&function&/span& &span class=&nx&&F&/span&&span class=&p&&(){}&/span&
&span class=&nx&&F&/span&&span class=&p&&.&/span&&span class=&nx&&prototype&/span& &span class=&o&&=&/span& &span class=&nx&&o&/span&&span class=&p&&;&/span&
&span class=&k&&return&/span& &span class=&k&&new&/span& &span class=&nx&&F&/span&&span class=&p&&()&/span&
&span class=&p&&}&/span&
&/code&&/pre&&/div&&blockquote& ----- 《JavaScript高级程序设计》P169&/blockquote&&br&所以从实现代码 return new F() 中我们可以看到,这依然是通过new来创建的。不同之处在于由 Object.create() 创建出来的对象没有构造函数,看到这里你是不是要问,没有构造函数我怎么知道它的__proto__指向哪里呢,其实这里说它没有构造函数是指在 Object.create() 函数外部我们不能访问到它的构造函数,然而在函数内部实现中是有的,它短暂地存在了那么一会儿。假设我们现在就在函数内部,可以看到对象的构造函数是F, 现在&br&&div class=&highlight&&&pre&&code class=&language-js&&&span class=&c1&&//以下是用于验证的伪代码&/span&
&span class=&kd&&var&/span& &span class=&nx&&f&/span& &span class=&o&&=&/span& &span class=&k&&new&/span& &span class=&nx&&F&/span&&span class=&p&&();&/span&
&span class=&c1&&//于是有&/span&
&span class=&nx&&f&/span&&span class=&p&&.&/span&&span class=&nx&&__proto__&/span& &span class=&o&&===&/span& &span class=&nx&&F&/span&&span class=&p&&.&/span&&span class=&nx&&prototype&/span& &span class=&c1&&//true&/span&
&span class=&c1&&//又因为&/span&
&span class=&nx&&F&/span&&span class=&p&&.&/span&&span class=&nx&&prototype&/span& &span class=&o&&===&/span& &span class=&nx&&o&/span&&span class=&p&&;&/span&&span class=&c1&&//true&/span&
&span class=&c1&&//所以&/span&
&span class=&nx&&f&/span&&span class=&p&&.&/span&&span class=&nx&&__proto__&/span& &span class=&o&&===&/span& &span class=&nx&&o&/span&&span class=&p&&;&/span&
&/code&&/pre&&/div& 因此由Object.create(o)创建出来的对象它的隐式原型指向o。好了,对象的创建方式分析完了,现在你应该能够判断一个对象的__proto__指向谁了。&br&&br&好吧,还是举一些一眼看过去比较疑惑的例子来巩固一下。&br&&br&&ul&&li& 构造函数的显示原型的隐式原型:&/li&&/ul&&ol&&li&内建对象(built-in object):比如Array(),Array.prototype.__proto__指向什么?Array.prototype也是一个对象,对象就是由 Object() 这个构造函数创建的,因此Array.prototype.__proto__ === Object.prototype //true,或者也可以这么理解,所有的内建对象都是由Object()创建而来。&/li&&/ol&&ul&&li&自定义对象&/li&&/ul&
默认情况下:&br&&div class=&highlight&&&pre&&code class=&language-text&&function Foo(){}
var foo = new Foo()
Foo.prototype.__proto__ === Object.prototype //true 理由同上
&/code&&/pre&&/div&
其他情况: &br&(1)&br&&div class=&highlight&&&pre&&code class=&language-js&& &span class=&kd&&function&/span& &span class=&nx&&Bar&/span&&span class=&p&&(){}&/span&
&span class=&c1&&//这时我们想让Foo继承Bar&/span&
&span class=&nx&&Foo&/span&&span class=&p&&.&/span&&span class=&nx&&prototype&/span& &span class=&o&&=&/span& &span class=&k&&new&/span& &span class=&nx&&Bar&/span&&span class=&p&&()&/span&
&span class=&nx&&Foo&/span&&span class=&p&&.&/span&&span class=&nx&&prototype&/span&&span class=&p&&.&/span&&span class=&nx&&__proto__&/span& &span class=&o&&===&/span& &span class=&nx&&Bar&/span&&span class=&p&&.&/span&&span class=&nx&&prototype&/span& &span class=&c1&&//true&/span&
&/code&&/pre&&/div&(2)&br&&div class=&highlight&&&pre&&code class=&language-js&&&span class=&c1&&//我们不想让Foo继承谁,但是我们要自己重新定义Foo.prototype&/span&
&span class=&nx&&Foo&/span&&span class=&p&&.&/span&&span class=&nx&&prototype&/span& &span class=&o&&=&/span& &span class=&p&&{&/span&
&span class=&nx&&a&/span&&span class=&o&&:&/span&&span class=&mi&&10&/span&&span class=&p&&,&/span&
&span class=&nx&&b&/span&&span class=&o&&:-&/span&&span class=&mi&&10&/span&
&span class=&p&&}&/span&
&span class=&c1&&//这种方式就是用了对象字面量的方式来创建一个对象,根据前文所述 &/span&
&span class=&nx&&Foo&/span&&span class=&p&&.&/span&&span class=&nx&&prototype&/span&&span class=&p&&.&/span&&span class=&nx&&__proto__&/span& &span class=&o&&===&/span& &span class=&nb&&Object&/span&&span class=&p&&.&/span&&span class=&nx&&prototype&/span&
&/code&&/pre&&/div&&b&注&/b&: 以上两种情况都等于完全重写了Foo.prototype,所以Foo.prototype.constructor也跟着改变了,于是乎constructor这个属性和原来的构造函数Foo()也就切断了联系。&br&&br&&ul&&li& 构造函数的隐式原型&br&&/li&&/ul& 既然是构造函数那么它就是Function()的实例,因此也就指向Function.prototype,比如 Object.__proto__ === Function.prototype&br&&br&&b&4. instanceof&br&&/b&instanceof 操作符的内部实现机制和隐式原型、显式原型有直接的关系。instanceof的左值一般是一个对象,右值一般是一个构造函数,用来判断左值是否是右值的实例。它的内部实现原理是这样的: &br&&div class=&highlight&&&pre&&code class=&language-text&&//设 L instanceof R
//通过判断
L.__proto__.__proto__ ..... === R.prototype ?
//最终返回true or false
&/code&&/pre&&/div& 也就是沿着L的__proto__一直寻找到原型链末端,直到等于R.prototype为止。知道了这个也就知道为什么以下这些奇怪的表达式为什么会得到相应的值了&br&&div class=&highlight&&&pre&&code class=&language-js&& &span class=&nb&&Function&/span& &span class=&k&&instanceof&/span& &span class=&nb&&Object&/span& &span class=&c1&&// true &/span&
&span class=&nb&&Object&/span& &span class=&k&&instanceof&/span& &span class=&nb&&Function&/span& &span class=&c1&&// true &/span&
&span class=&nb&&Function&/span& &span class=&k&&instanceof&/span& &span class=&nb&&Function&/span& &span class=&c1&&//true&/span&
&span class=&nb&&Object&/span& &span class=&k&&instanceof&/span& &span class=&nb&&Object&/span& &span class=&c1&&// true&/span&
&span class=&nb&&Number&/span& &span class=&k&&instanceof&/span& &span class=&nb&&Number&/span& &span class=&c1&&//false&/span&
&/code&&/pre&&/div&&br&文章参考:&a href=&///?target=https%3A///developerworks/cn/web/1306_jiangjj_jsinstanceof/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&JavaScript instanceof 运算符深入剖析&i class=&icon-external&&&/i&&/a&&br&&br&欢迎批评指正,交流学习~
正好这段时间在重新看这部分,写一篇回答来梳理一下吧。 __proto__(隐式原型)与prototype(显式原型) 1.是什么 显式原型 explicit prototype property: 每一个函数在创建之后都会拥有一个名为prototype的属性,这个属性指向函数的原型对象。 Note:通过…
如其它答案,基本有两种解决方案&br&1. Redis自带的PUB/SUB机制,即发布-订阅模式。这种模式生产者(producer)和消费者(consumer)是1-M的关系,即一条消息会被多个消费者消费,当只有一个消费者时即可以看做一个1-1的消息队列,但这种方式并不适合题主的场景。首先,数据可靠性的无法保障,题主的数据最终需要落库,如果消息丢失、Redis宕机部分数据没有持久化甚至突然的网络抖动都可能带来数据的丢失,应该是无法忍受的。其次,扩展不灵活,没法通过多加consumer来加快消费的进度,如果前端写入数据太多,同步会比较慢,数据不同步的状态越久,风险越大,当然可以通过channel拆分的方式来解决,虽然不灵活,但可以规避。这种方案更适合于对数据可靠性要求不高,比如一些统计日志打点。&br&&br&2. Redis的PUSH/POP机制,利用的Redis的列表(lists)数据结构。比较好的使用模式是,生产者lpush消息,消费者brpop消息,并设定超时时间,可以减少redis的压力。这种方案相对于第一种方案是数据可靠性提高了,只有在Redis宕机且数据没有持久化的情况下丢失数据,可以根据业务通过AOF和缩短持久化间隔来保证很高的可靠性,而且也可以通过多个client来提高消费速度。但相对于专业的消息队列来说,该方案消息的状态过于简单(没有状态),且没有ack机制,消息取出后消费失败依赖于client记录日志或者重新push到队列里面。&br&&br&最后再来看题主的需求,是希望先写Redis,再异步同步到mysql里面,期望数据的最终一致性。这样带来的好处是前端写的请求飞速啊(不用落盘当然快),问题是很复杂,而且不太合理。假设是合理的话,就应该选择一个更可靠的消息中间件,比如Redis作者开源的Disque,或者阿里开源RocketMQ,以及基于Golang的nsq等,Redis更适合用来存数据。&br&&br&为什么说题主的需求不合理?&br&类似于这种先写缓存再同步到DB的,目的是为了减少DB压力,提升前端API性能。题主的方案虽然能做到这两点,但忽略了根本的一点:数据不管存到哪里,都是用来访问(使用)的。但在题主的方案里,写入Redis的数据除了同步到DB里,不接任何访问量,并没有什么卵用,最后DB读的压力上来了,还得把数据重新LOAD回Redis里面,得不偿失。&br&&br&什么样的架构更合理?&br&只简单说一下。&br&异步写入,在百度、58同城使用很多,基本的架构是先抽象出对象访问层(或者只是缓存层),对外屏蔽数据来源(Redis、mysql、others)。对象访问层对于某类数据的格式是定义好的,写的请求来了直接写入缓存(Redis),这样前端的读请求就直接读缓存了,这样存入的数据就有意义(当然了,接了大大的读的量啊)。然后是数据落地(最终一致性问题),简单的可以读RDB文件(实时性不高),复杂一点的可以实现Redis的主从同步协议(实时性高于前一种)。第一种简单,效率低,第二种复杂,效率高。&br&&a href=&///?target=http%3A//mp./s%3F__biz%3DMzAxMjgyMTY1NA%3D%3D%26mid%3Didx%3D1%26sn%3Dfcdfe54fb6c302f203f3af44%23rd& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&从Redis谈起(三)—— Redis和消息中间件&i class=&icon-external&&&/i&&/a& 这边文章比较详细得讲解了Redis和消息中间件&br&&br&&p&谢谢,希望对大家有帮助。&/p&
如其它答案,基本有两种解决方案 1. Redis自带的PUB/SUB机制,即发布-订阅模式。这种模式生产者(producer)和消费者(consumer)是1-M的关系,即一条消息会被多个消费者消费,当只有一个消费者时即可以看做一个1-1的消息队列,但这种方式并不适合题主的场景。…
sublime text,有在使用的朋友赞同我一下
sublime text,有在使用的朋友赞同我一下
&p&首先对于排名第一大谈 nodejs 弱爆了,System Engine 才是吊的人做个冷嘲——真正做 nodejs 的如果不熟悉 v8 引擎和 libuv 其实只能是做作外围吧?而熟悉 v8 和 libuv 的哪一个没有扎实的 System Engine 基础(没个七八年的 C/C++ 项目基础能玩转 v8 和 libuv 的那真是少见了)?然后说出什么搞 nodejs 的人搞不清楚 websocket 消耗多少内存这种话你确定打击面没有太大?难道你身边有一些初级的 nodejs 开发人员给你造成了错觉然后你就优越到没边了?&/p&&br&&p&在这种角度就讨论技术,讨论的根本不是技术,而是自己的偏好,自己的圈子和好恶。调侃下可以,但是真的是一点营养都没有。而那些赞同他的人,我只想问你们真的有研究过 nodejs?&/p&&br&&p&分割线下面是之前的回答。&/p&&p&-----------------------------------------------&/p&&br&&p&说 nodejs 只是靠营销的是否太天真了些?当初 nodejs 出来的时候各种 BUG,我简单的测试其大文件传输都会出现各种问题。而同時期的其他阵营早就甩其几条街了。但是为什么却能一直不断发展壮大?难道仅仅靠所谓的营销和忽悠?&br&&br&如果只孤立的去考虑 nodejs 的异步库到底怎样怎样,实在是太片面了,难道 nodejs 里面就只提供了异步网络 IO?&br&&br&事实上,&b&nodejs 是提供了一套通用的异步基础设施,使得你可以基于此构建各种异步 API。&/b&异步网络 IO 只是其上的一个具体应用。而现在问题里提及的 twisted 实际上在这一点上根本不具有与 nodejs 的可比性!&br&&br&我选择 nodejs 的原因很大程度上是因为它是 JavaScript 的,这样一来在前后端我可以用同一种语言完成整个项目,这是极大的一个优势!另外,尽管 nodejs 不是唯一的也不是最早的基于 JavaScript 的服务端方案。但是它是同時期性能 JavaScript 阵营里最佳的。&br&&br&再加上 nodejs 底层的 libuv 设计很简单,非常容易扩展,而且 npm 又那么好用。因此开发效率急速上升。&br&&br&选择 nodejs 到底为什么,其实到了现在,许多人各自有各自的理由。但许多人都是因为他是&b&基于 JavaScript 的低成本&/b&解决方案。&br&&br&考虑 nodejs,不止是考虑它的一小部分 API,而是它带来的各方面的便捷。请不要太片面。&/p&
首先对于排名第一大谈 nodejs 弱爆了,System Engine 才是吊的人做个冷嘲——真正做 nodejs 的如果不熟悉 v8 引擎和 libuv 其实只能是做作外围吧?而熟悉 v8 和 libuv 的哪一个没有扎实的 System Engine 基础(没个七八年的 C/C++ 项目基础能玩转 v8 和 lib…
如果是保护知识产权的角度,脱离混淆的js加密是伪命题,无论怎么加密,如果不加以混淆手段保护,都没有意义。如同传统软件的加壳保护,js混淆给底层的加密算法加了最基本的保障,在js层面来说,混淆和加密一定是相辅相成的。&br&&br&&b&1、为什么需要js混淆&/b&&br&显而易见,是为了保护我们的前端代码逻辑。&br&在web系统发展早期,js在web系统中承担的职责并不多,只是简单的提交表单,js文件非常简单,也不需要任何的保护。&br&随着js文件体积的增大,为了缩小js体积,加快http传输速度,开始出现了很多对js的压缩工具,比如 uglify、compressor、clouser。。。它们的工作主要是&br&&ul&&li&合并多个js文件&br&&/li&&li&去除js代码里面的空格和换行&br&&/li&&li&压缩js里面的变量名&br&&/li&&li&剔除掉注释&/li&&/ul&&img src=&/3bb900f0f50ce922ec82a_b.png& data-rawwidth=&700& data-rawheight=&212& class=&origin_image zh-lightbox-thumb& width=&700& data-original=&/3bb900f0f50ce922ec82a_r.png&&压缩后的代码&br&&br&虽然压缩工具出发点都是为了减少js文件的体积,但是人们发现压缩替换后的代码已经比源代码可读性差了很多,间接起到了代码保护的作用,于是压缩js文件成为了前端发布的标配之一。但是后来市面上主流浏览器chrome、Firefox等都提供了js格式化的功能,能够很快的把压缩后的js美化,再加上现代浏览器强大的debug功能,单纯压缩过的js代码对于真正怀有恶意的人,已经不能起到很好的防御工作,出现了&防君子不防小人&的尴尬局面。&br&&img src=&/bab0bc1fb1ad588c18d46e5eeb425c6b_b.png& data-rawwidth=&700& data-rawheight=&250& class=&origin_image zh-lightbox-thumb& width=&700& data-original=&/bab0bc1fb1ad588c18d46e5eeb425c6b_r.png&&chrome开发者工具格式化之后的代码&br&&br&而在web应用越来越丰富的今天,伴随着浏览器性能和网速的提高,js承载了更多的工作,不少后端逻辑都在向前端转移,与此同时也让更多的不法分子有机可乘。在web模型中,js往往是不法分子的第一个突破口。知晓了前端逻辑,不法分子可以模拟成一个正常的用户来实施自己的恶意行为。所以,在很多登录、注册、支付、交易等等页面中,关键业务和风控系统依赖的js都不希望被人轻易的破解,js混淆应运而生。&br&&br&&b&2、js混淆是不是纸老虎&/b&&br&这是一个老生常谈的问题。实际上,代码混淆早就不是一个新鲜的名词,在桌面软件时代,大多数的软件都会进行代码混淆、加壳等手段来保护自己的代码。Java和.NET都有对应的混淆器。黑客们对这个当然也不陌生,许多病毒程序为了反查杀,也会进行高度的混淆。只不过由于js是动态脚本语言,在http中传输的就是源代码,逆向起来要比打包编译后的软件简单很多,很多人因此觉得混淆是多此一举。&br&&img src=&/6fabdec5e89b0c6d5724559_b.png& data-rawwidth=&700& data-rawheight=&539& class=&origin_image zh-lightbox-thumb& width=&700& data-original=&/6fabdec5e89b0c6d5724559_r.png&&.NET混淆器dotFuscator&br&&br&其实正是因为js传输的就是源代码,我们才需要进行混淆,暴露在外的代码没有绝对的安全,但是在对抗中,精心设计的混淆代码能够给破坏者带来不小的麻烦,也能够为防守者争取更多的时间,相对于破解来说,混淆器规则的更替成本要小得多,在高强度的攻防中,可以大大增加破解者的工作量,起到防御作用。从这个角度来讲,关键代码进行混淆是必不可少的步骤。&br&&br&&b&3、如何进行js混淆&/b&&br&js混淆器大致有两种:&br&&ul&&li&通过正则替换实现的混淆器&br&&/li&&li&通过语法树替换实现的混淆器&/li&&/ul&第一种实现成本低,但是效果也一般,适合对混淆要求不高的场景。第二种实现成本较高,但是更灵活,而且更安全,更适合对抗场景,我这里主要讲一下第二种。基于语法层面的混淆器其实类似于编译器,基本原理和编译器类似,我们先对编译器做一些基本的介绍。&br&&br&&b&名词解释&/b&&br&token: 词法单元,也有叫词法记号的,词法分析器的产物,文本流被分割后的最小单位。&br&AST: 抽象语法树,语法分析器的产物,是源代码的抽象语法结构的树状表现形式。&br&&img src=&/257d93a6adf869bf21f50978cabb876d_b.png& data-rawwidth=&700& data-rawheight=&533& class=&origin_image zh-lightbox-thumb& width=&700& data-original=&/257d93a6adf869bf21f50978cabb876d_r.png&&编译器VS混淆器&br&&br&&b&编译器工作流程&/b&&br&简单的说,当我们读入一段字符串文本(source code),词法分析器会把它拆成一个一个小的单位(token),比如数字1 是一个token, 字符串'abc'是一个token等等。接下来语法分析器会把这些单位组成一颗树状结构(AST),这个树状结构就代表了token们的组成关系。比如 1 + 2 就会展示成一棵加法树,左右子节点分别是token - 1 和token - 2 ,中间token表示加法。编译器根据生成的AST转换到中间代码,最终转换成机器代码。&br&&br&对编译器更多细节感兴趣的同学可以移步龙书:&a href=&///?target=https%3A///subject/5416783/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&编译原理&i class=&icon-external&&&/i&&/a&&br&&br&&b&混淆器工作流程&/b&&br&编译器需要把源代码编译成中间代码或者机器码,而我们的混淆器输出其实还是js。所以我们从语法分析之后往下的步骤并不需要。想想我们的目标是什么,是修改原有的js代码结构,在这里面这个结构对应的是什么呢?就是AST。任何一段正确的js代码一定可以组成一颗AST,同样,因为AST表示了各个token的逻辑关系,我们也可以通过AST反过来生成一段js代码。所以,你只需要构造出一颗AST,就能生成任何js代码!混淆过程如上右图所示&br&&br&通过修改AST生成一个新的AST,新的AST就可以对应新的JavaScript代码。&br&&br&&b&规则设计&/b&&br&知道了大致的混淆流程,最重要的环节就是设计规则。我们上面说了,我们需要生成新的AST结构意味着会生成和源代码不一样的js代码,但是我们的混淆是不能破坏原有代码的执行结果的,所以混淆规则必须保证是在不破坏代码执行结果的情况下,让代码变得更难以阅读。&br&&br&具体的混淆规则各位可以自行根据需求设计,比如拆分字符串、拆分数组,增加废代码等等。&br&&br&参考:提供商业混淆服务的&a href=&///?target=https%3A///& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&jscramble&i class=&icon-external&&&/i&&/a&的混淆规则&br&&img src=&/d9f7ace804d5e1a163e9_b.png& data-rawwidth=&700& data-rawheight=&476& class=&origin_image zh-lightbox-thumb& width=&700& data-original=&/d9f7ace804d5e1a163e9_r.png&&&br&&b&实现&/b&&br&很多人看到这里就望而却步,因为词法分析和文法分析对编译原理要求较高。其实这些现在都有工具可以帮助搞定了,借助工具,我们可以直接进行最后一步,对AST的修改。&br&&br&市面上JavaScript词法和文法分析器有很多,比如其实v8就是一个,还有mozilla的&a href=&///?target=https%3A//developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/Parser_API& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&SpiderMonkey&i class=&icon-external&&&/i&&/a&, 知名的&a href=&///?target=http%3A//esprima.org/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&esprima&i class=&icon-external&&&/i&&/a&等等,我这里要推荐的是&a href=&///?target=http%3A//lisperator.net/uglifyjs/transform& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&uglify&i class=&icon-external&&&/i&&/a&,一个基于nodejs的解析器。它具有以下功能:&br&&ul&&li&parser,把 JavaScript 代码解析成抽象语法树&/li&&li&code generator,通过抽象语法树生成代码&/li&&li&scope analyzer,分析变量定义的工具&/li&&li&tree walker,遍历树节点&/li&&li&tree transformer,改变树节点&br&&/li&&/ul&对比下我上面给出的混淆器设计的图,发现其实只需要修改语法树 这一步自己完成。&br&&br&&b&实例&/b&&br&说了这么多,可能很多人还是一头雾水,为了帮助各位理解,我准备了一个简单的例子,假设我们的混淆规则是想把 var a = 1; 中的数字1换成16进制,我们该如何设计混淆器呢。首先对源代码做词法分析和语法分析,uglify一个方法就搞定了,生成一颗语法树,我们需要做的就是找到语法树中的数字然后修改成16进制的结果,如下图所示:&br&&br&实例代码:&br&&div class=&highlight&&&pre&&code class=&language-js&&&span class=&kd&&var&/span& &span class=&nx&&UglifyJS&/span& &span class=&o&&=&/span& &span class=&nx&&require&/span&&span class=&p&&(&/span&&span class=&s2&&&uglify-js&&/span&&span class=&p&&);&/span&
&span class=&kd&&var&/span& &span class=&nx&&code&/span& &span class=&o&&=&/span& &span class=&s2&&&var a = 1;&&/span&&span class=&p&&;&/span&
&span class=&kd&&var&/span& &span class=&nx&&toplevel&/span& &span class=&o&&=&/span& &span class=&nx&&UglifyJS&/span&&span class=&p&&.&/span&&span class=&nx&&parse&/span&&span class=&p&&(&/span&&span class=&nx&&code&/span&&span class=&p&&);&/span& &span class=&c1&&//toplevel就是语法树&/span&
&span class=&kd&&var&/span& &span class=&nx&&transformer&/span& &span class=&o&&=&/span& &span class=&k&&new&/span& &span class=&nx&&UglifyJS&/span&&span class=&p&&.&/span&&span class=&nx&&TreeTransformer&/span&&span class=&p&&(&/span&&span class=&kd&&function&/span& &span class=&p&&(&/span&&span class=&nx&&node&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&k&&if&/span& &span class=&p&&(&/span&&span class=&nx&&node&/span& &span class=&k&&instanceof&/span& &span class=&nx&&UglifyJS&/span&&span class=&p&&.&/span&&span class=&nx&&AST_Number&/span&&span class=&p&&)&/span& &span class=&p&&{&/span& &span class=&c1&&//查找需要修改的叶子节点&/span&
&span class=&nx&&node&/span&&span class=&p&&.&/span&&span class=&nx&&value&/span& &span class=&o&&=&/span& &span class=&s1&&'0x'&/span& &span class=&o&&+&/span& &span class=&nb&&Number&/span&&span class=&p&&(&/span&&span class=&nx&&node&/span&&span class=&p&&.&/span&&span class=&nx&&value&/span&&span class=&p&&).&/span&&span class=&nx&&toString&/span&&span class=&p&&(&/span&&span class=&mi&&16&/span&&span class=&p&&);&/span&
&span class=&k&&return&/span& &span class=&nx&&node&/span&&span class=&p&&;&/span& &span class=&c1&&//返回一个新的叶子节点 替换原来的叶子节点&/span&
&span class=&p&&};&/span&
&span class=&p&&});&/span&
&span class=&nx&&toplevel&/span&&span class=&p&&.&/span&&span class=&nx&&transform&/span&&span class=&p&&(&/span&&span class=&nx&&transformer&/span&&span class=&p&&);&/span&
&span class=&c1&&//遍历AST树&/span&
&span class=&kd&&var&/span& &span class=&nx&&ncode&/span& &span class=&o&&=&/span& &span class=&nx&&toplevel&/span&&span class=&p&&.&/span&&span class=&nx&&print_to_string&/span&&span class=&p&&();&/span& &span class=&c1&&//从AST还原成字符串&/span&
&span class=&nx&&console&/span&&span class=&p&&.&/span&&span class=&nx&&log&/span&&span class=&p&&(&/span&&span class=&nx&&ncode&/span&&span class=&p&&);&/span& &span class=&c1&&// var a = 0x1;&/span&
&/code&&/pre&&/div&上面的代码很简单,首先通过parse方法构建语法树,然后通过TreeTransformer遍历语法树,当遇到节点属于UglifyJS.AST_Number类型(所有的AST类型见&a href=&///?target=http%3A//lisperator.net/uglifyjs/ast& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&ast&i class=&icon-external&&&/i&&/a&),这个token具有一个属性 value 保存着数字类型的具体值,我们将其改成16进制表示,然后 return node 就会用新的节点代替原来的节点。&br&&br&&b&效果展示&/b&&br&贴一个我自己设计的混淆器混淆前后代码:&br&&img src=&/870bb51cf2a82d694bf68a68b38c6657_b.png& data-rawwidth=&700& data-rawheight=&259& class=&origin_image zh-lightbox-thumb& width=&700& data-original=&/870bb51cf2a82d694bf68a68b38c6657_r.png&&&br&&b&4、混淆对性能的影响&/b&&br&由于增加了废代码,改变了原有的AST,混淆对性能肯定会造成一定的影响,但是我们可以通过规则来控制影响的大小。&br&&ul&&li&减少循环混淆,循环太多会直接影响代码执行效率&/li&&li&避免过多的字符串拼接,因为字符串拼接在低版本IE下面会有性能问题&/li&&li&控制代码体积,在插入废代码时应该控制插入比例,文件过大会给网络请求和代码执行都带来压力&/li&&/ul&我们通过一定的规则完全可以把性能影响控制在一个合理的范围内,实际上,有一些混淆规则反而会加快代码的执行,比如变量名和属性名的压缩混淆,会减小文件体积,比如对全局变量的复制,会减少作用域的查找等等。在现代浏览器中,混淆对代码的影响越来越小,我们只需要注意合理的混淆规则,完全可以放心的使用混淆。&br&&br&&b&5、混淆的安全性&/b&&br&混淆的目的是保护代码,但是如果因为混淆影响了正常功能就舍本逐末了。&br&由于混淆后的AST已经和原AST完全不同了,但是混淆后文件的和原文件执行结果必须一样,如何保证既兼顾了混淆强度,又不破坏代码执行呢?高覆盖的测试必不可少:&br&&ul&&li&对自己的混淆器写详尽的单元测试&br&&/li&&li&对混淆的目标代码做高覆盖的功能测试,保证混淆前后代码执行结果完全一样&br&&/li&&li&多样本测试,可以混淆单元测试已经完备了的类库,比如混淆 Jquery 、AngularJS 等,然后拿混淆后的代码去跑它们的单元测试,保证和混淆前执行结果完全一样&br&&/li&&/ul&&br&&b&本文节选自文章《&a href=&///?target=http%3A///community/art/show%3F%26articleid%3D503& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&可信前端之路-代码保护&i class=&icon-external&&&/i&&/a&》,作者:莫念@阿里安全&/b&
如果是保护知识产权的角度,脱离混淆的js加密是伪命题,无论怎么加密,如果不加以混淆手段保护,都没有意义。如同传统软件的加壳保护,js混淆给底层的加密算法加了最基本的保障,在js层面来说,混淆和加密一定是相辅相成的。 1、为什么需要js混淆 显而易见…
说得好像你懂得v8是怎么执行你写的JS的一样。&br&说得好像你懂得C++runtime是怎么执行V8的一样。&br&说得好像你懂得C++是怎么转换成机器码的一样。&br&&br&--------------------------&br&高级语言的意义就是把程序员从繁复的造轮子工作中解放出来。&br&要是这个道理都没高明白,我建议题主还是多去思考思考为什么jQuery这种东西会出现和流行。&br&&br&---------------------------&br&我在评论里说&br&&blockquote&原生dom操作和jQuery都是浏览器渲染的接口&/blockquote&可能有人不理解我为什么这么讲。&br&当然后来想想我这么说是不太恰当。但是你就此认为我不知道jQuery是用javascript写的是不是就有点过分了?&br&&br&不过我也不是随便乱说的。&br&&b&接口(Interface)是什么?是约定。&/b&你和其他程序(模块,类……)约定好了,这个动作的结果该是什么样子,比如返回一个值,或者在UI上做一个什么变换,然后这个动作就不归你管了,由其他程序去执行。&br&&br&所以,jQuery操作dom算不上什么原理,javascript操作dom也算不上原理。&br&这都是浏览器给你的dom操作接口,至于为什么你做了个动作屏幕上的东西变了,那是浏览器做的事情。&br&区别的地方也就是两点:&br&1、jQuery是在原生dom操作的基础上封装出来的。&br&2、jQuery不是浏览器自带的。&br&可是,难道第三方接口就不是接口了?(说它是浏览器的接口可能不准确,因为带有一种钦定的感觉)&br&&br&题主讲的Js的原理,我理解成dom渲染原理,这里问题不大吧。&br&&b&其实你用javascript去操作dom,能学到的大部分是技巧,不是原理。&/b&&br&因为渲染的工作是浏览器做的,不是你做的。&br&你的工作,也就是相当于给浏览器写了个配置文件,告诉它我要的效果是这个样子。&br&&br&&br&&b&这种程度,离原理还差得远。&/b&&br&我举个例子,任何一个语言或者平台,总有一些“最佳实践”。记住了,并且会用——这叫技巧;知道为什么这个东西在这是最佳——这叫原理。&br&最简单的,你需要反复操作一个dom的话,最后把它保存在一个变量里——这是技巧;因为如果你不这么做浏览器要反复的查询dom树,做无用功,这里只用很少的空间就换取了大量的时间——这是原理。
说得好像你懂得v8是怎么执行你写的JS的一样。 说得好像你懂得C++runtime是怎么执行V8的一样。 说得好像你懂得C++是怎么转换成机器码的一样。 -------------------------- 高级语言的意义就是把程序员从繁复的造轮子工作中解放出来。 要是这个道理都没高明白…
发现这个问题也曾经被邀请回答过。今天还没睡着就来说点我的想法。&br&&br&“React.js 到底是迎合部分人口味还是真的是自然而然产生的?”&br&&br&这个问题的提法呢,其实有问题。为什么“迎合部分人口味”跟“自然而然产生”是互斥的?&br&&br&大家知道除了Angular党、Vue党之外,我是少数现在仍然不怎么用React的人之一。此外,我也是从强调内容、样式、行为分离的那个年代过来的老前端。许多那个年代过来的人对React的第一印象是这不是开倒车吗。&br&&br&但是在我第一次知道React的时候,虽然我也对这个方向持保留态度(一直至今),但是我并没有非常意外。包括React后来提出的CSS in JS我也一点不意外(参见&a href=&///?target=https%3A///hax//issues/22& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&如何看待《React: CSS in JS》? · Issue #22 · hax/ · GitHub&i class=&icon-external&&&/i&&/a&)。&br&&br&&a data-hash=&20fdd386a6e59d178b8fe14e2863cb40& href=&///people/20fdd386a6e59d178b8fe14e2863cb40& class=&member_mention& data-editable=&true& data-title=&@张克军& data-hovercard=&p$b$20fdd386a6e59d178b8fe14e2863cb40&&@张克军&/a& 是国内前端圈领军人物中最早尝试和安利React的。我记得有次技术会议后的饭局,我们一群人聊到组件化和React,我就跟他说,我觉得React是比较适合(迎合)JS老手的框架。他当时表示不太理解我讲的观点,他觉得React的学习曲线也并不高。&br&&br&其实我并不是指学习曲线的问题,而讲的是我的深层的感觉。React为什么JS老手会喜欢,因为JS老手对JS的掌握很纯熟,按说应该和其他语言纯熟程度同等水平的工程师对问题领域的掌控程度相当,但是前端领域却并非如此,就算你JS很牛,面对看似简单的html/css却总是感觉力有不逮和受到诸多限制。有一些限制是浏览器的bug和兼容性问题,更有一些限制是web标准本身先天决定的。比如难用的dom api,比如组件的封装性,比如css的复用和局域化。&br&&br&虽然复杂web应用的大部分代码是JS,但是整个程序的架构却强行被拆散到各个页面,而js又随着被拆散的页面而到处散布。即使js本身的模块化实践早已有之,但涉及UI的部分,所有抽象的努力,最后都要终止在html/css的边界,也就是UI的抽象复用单元总感觉包不住,要泄露到html/css里。&br&&br&而React有一个重要的突破,他把html/js的控制方向反转了。这给JS老手一下子进入一个自由天地的感觉。html/css不再是很难处理且反过来约束js的异质物,而成为了一样可以用js操控的普通对象。&br&&br&有人可能觉得最终面对的问题本质总是一样的嘛。确实如此,但是在本来的方式里,UI是一个很特殊的问题,以至于在以前的许多公司分出了重构和JS前端两种岗位,其中重构是更靠近设计的体系,它并不在应用编程架构中,它所划定的领域知识和技能的要求,许多流程、范式、评价方式,都跟普通编程很不一样。&br&&br&但是React使得可以把UI纳入到编程的体系里,理顺了关系。为了这一点,React也提供了特性来降低对Web UI领域知识的要求,比如声明式的jsx消除了DOM api,后来的css in js,消除了cascade机制。&br&&br&还有React为什么只做view?因为在原本的体系里这是JS老手最hold不住的地方。早一点的Angular就不一样。JS老手其实大都不喜欢Angular,因为虽然它解决了大量问题,但是像应用架构本身JS老手们都有自己的看法和喜好,很可能早就用上了Backbone或内部自制的框架。甚至JS老手早都用上了各类模块化方案了,但Angular的模块化还一团糟。相比较而言,只做view的React就更少理由被抗拒。&br&&br&要之,我的看法是,React当然是迎合了一部分人的口味(同时也不对另一批人的口味),但是不是普通的某些人,其中很多是JS老手,身份上更靠近通用编程的程序员,而不是靠近(在有Angular/React之前的传统)前端。React同时也是自然而然产生的。你觉得它不自然,只是因为你不在它的对味人群中。&br&&br&BTW,&a data-hash=&cfdec6226ece879d2571fbcf& href=&///people/cfdec6226ece879d2571fbcf& class=&member_mention& data-editable=&true& data-title=&@尤雨溪& data-hovercard=&p$b$cfdec6226ece879d2571fbcf&&@尤雨溪&/a& 提到的函数式编程的卖点,虽然现在是很热,但是与其说是React因此而火,不如说是函数式因为React社区的卖力宣传而被大家重视了。&br&&br&最后,我虽然不用React,但是我对React的评价其实很高。至于为什么我不用,不是本问题的scope,就不展开了。
发现这个问题也曾经被邀请回答过。今天还没睡着就来说点我的想法。 “React.js 到底是迎合部分人口味还是真的是自然而然产生的?” 这个问题的提法呢,其实有问题。为什么“迎合部分人口味”跟“自然而然产生”是互斥的? 大家知道除了Angular党、Vue党之外…
最近此类问题在知乎上看到很多,我觉得可能很多在找工作的同学都走进了一个怪圈,一直想知道我究竟要会哪些工具,懂哪些知识,才能拿到好的 offer。我不敢说这是一个误区,但我想谈谈我的一些看法,可能不尽正确,也算提供另外一种思路吧。&br&&br&同样知道和不知道题主所说的这些知识的人,得到的大公司的评价可能差得非常多。我想说的不是面试的运气,而是如果有人觉得大公司面试面的是你会不会这个工具那个工具,那么这么想的人的认知本身可能就和大公司的期望存在不小的差距。&br&&br&你可以完全不会 less,如果你把 sass 玩得很溜。你可以把 http 状态码背得烂熟于胸,但这未必就给你加分多少,因为完全可能是面试前五分钟现记的。没错,掌握的知识点、会的工具多,总是加分项,但除非你是天才,大多数人的精力总是有限的。所以面试官问这些,并不是担心你进来来不及学某某工具(大公司一般都等得起,前提是你是值得等的人),而是因为结果是很好的展现过程的手段:通过你学习的成果,反推你是怎么学习的,反推你是怎样的人,从而判断是不是符合公司价值观和需要的。&br&&br&所以,除了这些工具,更应该关注的是自己的软实力。可能有人觉得说到软实力就很虚很装很扯,说得好像程序员只有代码是真本事一样。其实会这么想的程序员大多是对自己的软实力没自信罢了。&br&&br&我不打算介绍面试时候包装自己的技巧,并不是因为我小气打算自己留着用…而是这就像谈恋爱一样,为了良好的第一印象可以选择和对方有共鸣的方面去表现,但是切忌为了争取机会而说一些和自己价值观不相符的地方。坑到公司人家大不了开除你,你本来就是几千几万分之一,坑到自己代价多大啊?&br&我聊过的 BAT 几个老大对软实力方面都是非常看重的,事实上如果他们这都不看重,我会怀疑这个团队到底靠不靠谱。尤其是因为作为校招生,就算学校里做过什么真实的项目,跟公司的业务比起来都算不上什么经验的。所以没有实际经验可以看出能力的时候,当然就看潜力了。&br&但这潜力一定是要落到实处的。怎么说呢?比如如果你说“你问的这些库我都知道但都没用过,因为我太忙了。不过我学习能力非常强,我进团队马上就能学会的。”如果我是面试官,我会对这样的答案非常怀疑,我比较偏结果主义者,我会怀疑,如果你真学习能力强,都学了这么久了,怎么也应该做出点什么好玩的东西了吧,至少怎么也应该学到点什么了吧。相比这样的答案,当我不会的时候,我会说明我的时间都花在学什么上面了,有哪些思考,有哪些收获,有哪些可能是和那些我没学到的库相通的或者互补的。我觉得这些背后的思考才是所谓的学习能力的佐证,而不是说“我学习能力很强的,两三天就能学会”就能有的说服力。&br&除了学习能力,还有非常多的软实力,比如沟通能力思考能力等等。这些我就不细说了,因为一两年内似乎很难见效的,或许这更是这些软实力难能可贵的地方吧!就我个人而言,我觉得比较好玩也没想到的事是,十几年主持晚会的经验带给我的锻炼最近非常显著也意外地让我觉得很受用。因为这种多年的锻炼,不仅仅是对口才,更多的是敢于表现自己的勇气,和善于表达自己的方式,以及永远的台上三分钟台下半年工的觉悟。&br&这些软实力可能临时抱佛脚不太现实,但我这里是想提醒那些软实力还不错的同学,别忘记展现自己的这一面。&br&&br&最后,安得 offer 千万发,大庇天下软狗俱欢颜。
最近此类问题在知乎上看到很多,我觉得可能很多在找工作的同学都走进了一个怪圈,一直想知道我究竟要会哪些工具,懂哪些知识,才能拿到好的 offer。我不敢说这是一个误区,但我想谈谈我的一些看法,可能不尽正确,也算提供另外一种思路吧。 同样知道和不知…
&p&python对C语言的兼容性比js要强,这基本上是最后一个阵地了,所以你可以发现计算密集型的事情大家还是愿意用python来当浆糊糊C/C++的代码,而不是js来糊。&/p&&p&讲道理,如果js把那些令人惊喜的feature修掉了(什么a&=b && a&=b 不相当于a==b啦,什么fuck instance of (eval(typeof fuck))不恒为true啦,还有&1&+2不崩溃啦,变量声明自动提前而不是爆编译错误啦(你都知道要提前了为什么不干脆报错)),哪一点不比python强?&/p&
python对C语言的兼容性比js要强,这基本上是最后一个阵地了,所以你可以发现计算密集型的事情大家还是愿意用python来当浆糊糊C/C++的代码,而不是js来糊。讲道理,如果js把那些令人惊喜的feature修掉了(什么a&=b && a&=b 不相当于a==b啦,什么fuck instanc…
已有帐号?
无法登录?
社交帐号登录}

我要回帖

更多关于 中央很多人讨厌薄一波 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信