首页
Search
1
Linux 下 Bash 脚本 bad interpreter 报错的解决方法
69 阅读
2
Arch Linux 下解决 KDE Plasma Discover 的 Unable to load applications 错误
51 阅读
3
Arch Linux 下解决 KDE Plasma Discover 的 Unable to load applications 错误
42 阅读
4
如何在 Clash for Windows 上配置服务
40 阅读
5
如何在 IOS Shadowrocket 上配置服务
40 阅读
clash
服务器
javascript
全部
游戏资讯
登录
Search
加速器之家
累计撰写
1,061
篇文章
累计收到
0
条评论
首页
栏目
clash
服务器
javascript
全部
游戏资讯
页面
搜索到
1061
篇与
的结果
2024-09-10
Stream-Adventure HTML-Stream 中对 trumpet 的理解
玩了 John Resig 提到的 Stream-Adventure ,在 HTML-Stream 关卡中,根据题目提示写出了代码var through = require('through') , trumpet = require('trumpet') function upper(buf) { this.queue(buf.toString().toUpperCase()) } var tr = trumpet() process.stdin.pipe(tr).pipe(process.stdout) var stream = tr.select('.loud').createStream() stream.pipe(through(upper)).pipe(stream)虽然写出来了,但其实我还是混淆着 tr 和 steam 流。最后在 nodeschool 的讨论中得到了比较好启发。试着整理一下自己的表述:这里很明显是有两条流 tr 和 steam ,之前混淆他们的关系,所以一直不能理解。tr 是主流,输入流入 tr 最后流到输出stream 流是在 tr 内部发生的,tr 会尝试找到 loud 类的内容,然后创建流 stream ,流向 through 改变大小写,最后流回 stream 覆盖原来的内容。
2024年09月10日
4 阅读
0 评论
0 点赞
2024-09-10
JavaScript 函数名里面有什么?
原文:What's in a Function Name?(2014-11-25)每次为 JSHint 提交代码我都会学到一些 JavaScript 的新东西。最近的一次知识之旅中我接触到了函数对象的 name 属性。JSHint 有一个很有意思但很少人知道的功能:代码分析报告。当以编程方式使用时,JSHint 会返回一个对象,描述已分析代码的数据。它包括(但不限于)代码中函数对象的信息:jshint('function myFn() {}'); console.log(jshint.data().functions); /* [{ name: 'myFn', param: undefined, line: 1, character: 15, last: 1, lastcharacter: 19, metrics: { complexity: 1, parameters: 0, statements: 0 } }] */JSHint 网站本身实时生成的“Metrics”报告是这个功能最突出的使用。比如:Metrics There is only one function in this file. It takes no arguments. This function is empty. Cyclomatic complexity number for this function is 1. 我得知这个功能在与一个不相关的 bug 一起工作时会出错。更困扰我的是,我发现自己以前对 JavaScript 函数名的理解完全是错误的。对我的三观质疑了几个小时(“名字意味着什么?”、“我是真实存在的么?”....)之后,我决定开始研究这个 issue 并一劳永逸地学习正确的理解。下面是我所学到的东西。你以为自己知道...首先我该解释一下我一开始对 name 如何在 JavaScript 中分配的误解。我以前只知道函数对象间的一个区别 —— 函数声明与函数表达式。前者需要一个标识符,所以我通常认为这是一个“命名函数”:function myFunction() { }而后者不需要标识符,我就把它叫作“匿名函数”:(function() { }());这种推断在直觉上是合理的,因为它用了直白的词语定义如“命名的”和“匿名的”。这也许可以解释为何不止我一个人存在这个误解。事实上:现在的 JavaScript (ECMAScript 5.1 或缩写 ES5)对于函数的 name 属性并没有明确的说明。稍微看看这个相关的说明可以支持我的观点。我们一般认为命名函数表达式中的标识符会指向“name”,实际上它只会被用在环境记录(environment record)中创建一个入口(entry),(跟 var 声明一样)。除此以外的说明都会存在平台特定的细微差异。(三观已崩溃)...你根本不知道碰巧下一代 JavaScript (即 ES6 ,工作草案在这里)的说明中明确了函数 name 属性的初始化。很方便的是,它完全是依赖于一个叫 SetFunctionName 的抽象操作。学习函数名赋值的来龙去脉其实就是简单(虽然也比较无聊)地去研究草案中关于这个操作的所有文献。对于平台实现者来说这必不可少,但对于我们来说,稍微学习几个例子就足够了。首先,这个规范形式化了一些我们可以预料到的行为:// 函数形式 ........................ `name` 属性的值 function myFunc() {} // 'myFunc; (function() {}()); // ''但远不止如此!这份规范还列出了一系列情况下,函数表达式(我前面所以为的“匿名函数”)也应该被赋予 name 值:// 函数形式 ........................ `name` 属性的值 new Function(); // 'anonymous' var toVar = function() {}; // 'toVar' (function() {}).bind(); // 'bound' var obj = { myMethod: function() {}, // 'myMethod' get myGetter() {}, // 'get myGetter' set mySetter(value) {} // 'set mySetter' };但这里要清楚,新规范只会在上面的这些情况下才改变函数对象的 name 属性。至于现在的 ES5 语法,环境记录依然会保持不变,只有函数声明才会产生新入口。这让我很惊讶,因为不像函数声明,我从来没有想到对变量或属性的赋值会与函数对象的创建有联系。但 ES6 就是这么任性!JSHint 团队将这个行为称作“名推断”(name inference)。函数对象本身没有让标识符定义,而是由运行时通过其初始赋值去对函数的名字做“最佳猜测”。最后,ES6 定义了一大堆不兼容 ES5 的新代码格式。当中一部分进一步扩展了函数名推断的语义:// 函数形式 ........................ `name` 属性的值 let toLet = function() {}; // 'toLet' const toConst = function() {}; // 'toConst' export default function() {} // 'default' function* myGenerator() {} // 'myGenerator' new GeneratorFunction() {} // 'anonymous' var obj = { ['exp' + 'ression']: function() {}, // 'expression' myConciseMethod() {}, // 'myConciseMethod' *myGeneratorMethod() {} // 'myGeneratorMethod' }; class MyClass { constructor() {} // 'MyClass' myClassMethod() {} // 'myClassMethod' }最后一个例子让我很吃惊,构造函数居然被赋予类的名字而不是“constructor”?对于其它大多数的类方法,其 name 值跟你想的基本一样。但构造方法很特殊,因为他们本质上都是引用其归属的类。这在 ES5 中也有相应的例子:function MyClass() {} MyClass.prototype.constructor === MyClass;这个原则同样适用于 ES6 ,即便构造函数体与 class 关键字出现在不同的表达式中。标准偏差手中有了完整的规格书,我们就可以重新看看 JSHint 中函数名推断的过程。注意它也不是一模一样照搬实现的,有些地方我们是故意做得跟规范不同。「表达式」:很多情况下,实现者会直接以表达式的结果去调用 SetFunctionName 。(如:“设 propKey 为对 PropertyName 求值的结果。[…] SetFunctionName(propValue, propKey) 。”)且因为 JSHint 是一个静态分析工具,它不会对检测的代码做任何计算(见文末)。所以这种情况下,我们会报告此函数的 name 为“(expression)”。「未命名」:规范要求“若 description 值为 undefined ,则 name 值取空字符串。”这里意思是类似下面的函数声明:(function() { })();其 name 值应该为 “” 。但我们决定对这种函数的 name 报告为“(empty)”。因为 JSHint 这个工具的目的是协助开发者而不是 JavaScript runtime ,我们觉得在这种情况下将规范作重新解释是可以接受的。具体来说:JSHint 在其报告中赋予函数的名字不会引起兼容性问题,所以我们实现了不同的行为,因为这样做更有帮助。改进的函数名推断已经在 JSHint 的 master 分支中 landed 了,可以展望它会出现在下个 release 中。其它名字的函数我从不厌倦去阅读下一代 JavaScript 的各种炫酷新特性。即便如此,对比起 generator、class、module 和 promise ,函数名的确显得有些过时。悲观者甚至会认为这是语言中的一个没有必要的累赘。但正如任何优秀的标准,这个新特性实际上也是一种现实需求的体现。报错的栈跟踪里需要函数名。缺少函数名推断的情况下,平台一般会用一些通用的替换值如“(anonymous function)”去报告没有名字的函数。这往往会从整体上削弱了栈跟踪的实用性。现在的一些性能检测工具和控制台会识别一个叫 displayName 的非标准值,并在栈跟踪时回退到该值。Malte Ubl 最近也赞成将此纳入 JavaScript 库代码,Ember.js 也对此稍作尝试。但随着 runtime 实现了新功能,诸如此类的非标准方法就变得没什么必要了。这个小小的改变可以帮助开发者专注于着手解决问题而无需担心怎么减少调试陷阱。所以即使在即将到来的各种 JavaScript 会议中你不太可能会见到标题为“ES6 中的函数名推断”之类的演讲,这个小小的特性依然值得庆祝。 JSHint 的确会对封闭字符串做连接操作,但这基本算不上是代码执行。
2024年09月10日
3 阅读
0 评论
0 点赞
2024-09-10
JavaScript 有必要缓存 for 循环中的 Array.length 吗?
问题缓存 Array.length 是老生常谈的小优化。// 不缓存 for (var i = 0; i
2024年09月10日
3 阅读
0 评论
0 点赞
2024-09-10
理解 Prototype
Prototype 初看很好理解,实际上很容易混淆。而且因为不影响平时使用,一直没用动力去了解,长时间都是在一知半解的状态。混淆混淆主要是因为 [[prototype]] 和 prototype 名字长得太像。看回以前总结的模拟继承笔记,基本就是照搬书,压根没搞清楚。首先是 [[prototype]], 每个对象都会有 [[prototype]]属性,它的本质就是指向另外一个对象。然后是 prototype。prototype 是 function 对象特有的属性。每个 function 都有一个 prototype(同时也会有 [[prototype]])。prototype 里有个叫 constructor 的属性,一般情况下就是指回这个 function。获取ES5 提供 Object.getPrototypeOf() 方法来获取一个对象的 [[prototype]];也可以访问 Object.prototype.__proto__,而且这个很早就存在了,但在 ES2015 才标准化。创建New当 new 一个 function 的时候,返回的那个新对象里的 [[prototype]] 就会指向这个 function 的 prototype。function Person () { // ... } var p = new Person() Object.getPrototypeOf(p) === Person.prototype // trueObject.create()ES5 提供了 Object.create() 方法来创建对象,它的第一个参数就是 prototype ,创建的新对象的 [[prototype]] 会指向这个参数。var obj = {} Object.getPrototypeOf(Object.create(obj)) === obj // true在《JavaScript高级程序设计》提到了这个方法的 polyfill ,当然,当时也是糊里糊涂地跟着实现了一遍,不怎么明白。这里需要理解 Object.create() 干了什么。它返回了一个对象,这个对象可以指定 [[prototype]]。在 ES2015 之前只能通过 new 来赋予一个对象 [[prototype]]。所以这个 polyfill 的核心思想就是利用一个空函数来改梁换柱。下面是简化的代码:function create (proto/* , 第二个参数 */) { // ... var Fn = function () {} Fn.prototype = proto || {} var obj = new Fn() // ... return obj } var obj = {} Object.getPrototypeOf(create(obj)) === obj // trueObject.setPrototypeOf()ES2015 增加了 Object.setPrototypeOf() 来设定对象的 [[prototype]],但是尽量不要用,可能会有性能问题。判断instanceof 和 isPrototypeOf() 都可以判断,但两者不一样。object instanceof constructor 沿着 constructor.prototype 来搜。也就是说 constructor 只能是函数。prototypeObj.isPrototypeOf(object) 是基于对象,不一定是函数。function Father () {} var f = new Father() f instanceof Father // true f instanceof Father.prototype // Error Father.prototype.isPrototypeOf(f) // true Father.isPrototypeOf(f) // false function Child () {} Child.prototype = f var c = new Child() c instanceof Father // true c instanceof f // Error f.isPrototypeOf(c) // true Father.isPrototypeOf(c) // fasle Father.prototype.isPrototypeOf(f) // true继承Child.prototype = Father.prototype 不久好了么,收工。这里的问题看出来了么。这种方式最大的问题是当在 Child 的 prototype 上增加一些属性的时候,会影响到 Father 去了,因为它们是同个对象。那好吧,Child.prototype = new Father() 搞定。这里的问题就更隐蔽一些。首先,我们只想要 prototype,这种方式会返回 Father 的一个对象,可能会增加一些没必要的属性;其次,Father 本质是一个函数,如果在 Father 里如果做了其它一些操作,比如改变了闭包或者全局变量什么的,new 的时候就会执行,有时候我们不希望这样。所以,更好的方法就是用 Object.create() 创建一个 [[prototype]] 指向父类 prototype 的对象,然后再手动指定 constructor。function Father () {} Father.prototype.isHandsome = function () { return true } function Child () {} Child.prototype = Object.create(Father.prototype) Child.prototype.constructor = Child var c = new Child() c.isHandsome() // true[完]
2024年09月10日
4 阅读
0 评论
0 点赞
2024-09-10
JavaScript this 的六道坎
鉴于this风骚的运作方式,对this的理解是永不过时的话题,本文试图通过将其大卸六块来钉住这个磨人的妖精。首先this is all about context.this说白了就是找大佬,找拥有当前上下文(context)的对象(context object)。大佬可以分为六层,层数越高权力越大,this只会认最大的。第一层:世界尽头权力最小的大佬是作为备胎的存在,在普通情况下就是全局,浏览器里就是window;在use strict的情况下就是undefined。function showThis () { console.log(this) } function showStrictThis () { 'use strict' console.log(this) } showThis() // window showStrictThis() // undefined第二层:点石成金第二层大佬说白了就是找这个函数前面的点.。如果用到this的那个函数是属于某个 context object 的,那么这个 context object 绑定到this。比如下面的例子,boss是returnThis的 context object ,或者说returnThis属于boss。var boss = { name: 'boss', returnThis () { return this } } boss.returnThis() === boss // true下面这个例子就要小心点咯,能想出答案么?var boss1 = { name: 'boss1', returnThis () { return this } } var boss2 = { name: 'boss2', returnThis () { return boss1.returnThis() } } var boss3 = { name: 'boss3', returnThis () { var returnThis = boss1.returnThis return returnThis() } } boss1.returnThis() // boss1 boss2.returnThis() // ? boss3.returnThis() // ?答案是boss1和window哦,猜对了吗。只要看使用this的那个函数。在boss2.returnThis里,使用this的函数是boss1.returnThis,所以this绑定到boss1;在boss3.returnThis里,使用this的函数是returnThis,所以this绑定到备胎。要想把this绑定到boss2怎么做呢?var boss1 = { name: 'boss1', returnThis () { return this } } var boss2 = { name: 'boss2', returnThis: boss1.returnThis } boss2.returnThis() //boss2没错,只要让使用this的函数是属于boss2就行。第三层:指腹为婚第三层大佬是Object.prototype.call和Object.prototype.apply,它们可以通过参数指定this。(注意this是不可以直接赋值的哦,this = 2会报ReferenceError。)function returnThis () { return this } var boss1 = { name: 'boss1' } returnThis() // window returnThis.call(boss1) // boss1 returnThis.apply(boss1) // boss1第四层:海誓山盟第四层大佬是Object.prototype.bind,他不但通过一个新函数来提供永久的绑定,还会覆盖第三层大佬的命令。function returnThis () { return this } var boss1 = { name: 'boss1'} var boss1returnThis = returnThis.bind(boss1) boss1returnThis() // boss1 var boss2 = { name: 'boss2' } boss1returnThis.call(boss2) // still boss1第五层:内有乾坤一个比较容易忽略的会绑定this的地方就是new。当我们new一个函数时,就会自动把this绑定在新对象上,然后再调用这个函数。它会覆盖bind的绑定。function showThis () { console.log(this) } showThis() // window new showThis() // showThis var boss1 = { name: 'boss1' } showThis.call(boss1) // boss1 new showThis.call(boss1) // TypeError var boss1showThis = showThis.bind(boss1) boss1showThis() // boss1 new boss1showThis() // showThis第六层:军令如山最后一个法力无边的大佬就是 ES2015 的箭头函数。箭头函数里的this不再妖艳,被永远封印到当前词法作用域之中,称作 Lexical this ,在代码运行前就可以确定。没有其他大佬可以覆盖。这样的好处就是方便让回调函数的this使用当前的作用域,不怕引起混淆。所以对于箭头函数,只要看它在哪里创建的就行。如果对 V8 实现的词法作用域感兴趣可以看看这里。function callback (cb) { cb() } callback(() => { console.log(this) }) // window var boss1 = { name: 'boss1', callback: callback, callback2 () { callback(() => { console.log(this) }) } } boss1.callback(() => { console.log(this) }) // still window boss1.callback2(() => { console.log(this) }) // boss1下面这种奇葩的使用方式就需要注意:var returnThis = () => this returnThis() // window new returnThis() // TypeError var boss1 = { name: 'boss1', returnThis () { var func = () => this return func() } } returnThis.call(boss1) // still window var boss1returnThis = returnThis.bind(boss1) boss1returnThis() // still window boss1.returnThis() // boss1 var boss2 = { name: 'boss2', returnThis: boss1.returnThis } boss2.returnThis() // boss2如果你不知道最后为什么会是 boss2,继续理解“对于箭头函数,只要看它在哪里创建”这句话。参考 Mozilla Developer Network Kyle Simpson, this & object prototypes Axel Rauschmayer, Speaking JavaScript [完]
2024年09月10日
5 阅读
0 评论
0 点赞
1
...
109
110
111
...
213