Skip to main content

One post tagged with "javascript"

View All Tags

· One min read

https://github.com/axa-group/nlp.js

NLP.js

"NLP.js" is a general natural language utilities for nodejs. Currently supporting:

  • Guess the language of a phrase
  • Fast levenshtein distance of two strings
  • Search the best substring of a string with less levenshtein distance to a given pattern.
  • Get stemmers and tokenizers for several languages.
  • Sentiment Analysis for phrases (with negation support).
  • Named Entity Recognition and management, multilanguage, and accepting similar strings, so the introduced text does not need to be exact.
  • Natural Language Processing Classifier, to classify utterance into intents.
  • Natural Language Generation Manager, so from intents and conditions it can generate an answer.
  • NLP Manager: a tool able to manage several languages, the Named Entities for each language, the utterance and intents for the training of the classifier, and for a given utterance return the entity extraction, the intent classification and the sentiment analysis. Also, it is able to maintain a Natural Language Generation Manager for the answers.

· One min read

javascript的变量声明具有hoisting机制,JavaScript引擎在执行的时候,会把所有变量的声明都提升到当前作用域的最前面。

先看一段代码

var v = "hello";
(function(){
console.log(v);
var v = "world";
})();

这段代码运行的结果是什么呢? 答案是:undefined 这段代码说明了两个问题, 第一,function作用域里的变量v遮盖了上层作用域变量v。代码做少些变动

var v = "hello";
if(true){
console.log(v);
var v = "world";
}

输出结果为"hello",说明javascript是没有块级作用域的。函数是JavaScript中唯一拥有自身作用域的结构

第二,在function作用域内,变量v的声明被提升了。所以最初的代码相当于:

var v = "hello";
(function(){
var v; //declaration hoisting
console.log(v);
v = "world";
})();

声明、定义与初始化

声明宣称一个名字的存在,定义则为这个名字分配存储空间,而初始化则是为名字分配的存储空间赋初值。 用C++来表述这三个概念

extern int i;// 这是声明,表明名字 i 在某处已经存在了
int i;// 这是声明并定义名字 i, 为 i 分配存储空间
i = 0;// 这是初始化名字 i, 为其赋初值为 0

javascript中则是这样

var v;// 声明变量 v
v = "hello";//(定义并) 初始化变量 v

因为javascript为动态语言,其变量并没有固定的类型,其存储空间大小会随初始化与赋值而变化,所以其变量的“定义”就不像传统的静态语言一样了,其定义显得无关紧要。

声明提升

当前作用域内的声明都会提升到作用域的最前面,包括变量和函数的声明

(function(){
var a = "1";
var f = function(){};
var b = "2";
var c = "3";
})();

变量a,f,b,c的声明会被提升到函数作用域的最前面,类似如下:

(function(){
var a,f,b,c;
a = "1";
f = function(){};
b = "2";
c = "3";
})();

请注意函数表达式并没有被提升,这也是函数表达式与函数声明的区别。进一步看二者的区别:

(function(){
//var f1,function f2(){}; //hoisting, 被隐式提升的声明

f1(); //ReferenceError: f1 is not defined
f2();

var f1 = function(){};
function f2(){}
})();

上面代码中函数声明f2被提升,所以在前面调用f2是没问题的。虽然变量f1也被提升,但f1提升后的值为undefined,其真正的初始值是在执行到函数表达式处被赋予的。所以只有声明是被提升的。

名字解析顺序

javascript中一个名字(name)以四种方式进入作用域(scope),其优先级顺序如下: 1、语言内置:所有的作用域中都有 this 和 arguments 关键字 2、形式参数:函数的参数在函数作用域中都是有效的 3、函数声明:形如function foo() {} 4、变量声明:形如var bar;

名字声明的优先级如上所示,也就是说如果一个变量的名字与函数的名字相同,那么函数的名字会覆盖变量的名字,无论其在代码中的顺序如何。但名字的初始化却是按其在代码中书写的顺序进行的,不受以上优先级的影响。看代码:

(function(){
var foo;
console.log(typeof foo); //function

function foo(){}

foo = "foo";
console.log(typeof foo); //string
})();

如果形式参数中有多个同名变量,那么最后一个同名参数会覆盖其他同名参数,即使最后一个同名参数并没有定义。

以上的名字解析优先级存在例外,比如可以覆盖语言内置的名字arguments。

命名函数表达式

可以像函数声明一样为函数表达式指定一个名字,但这并不会使函数表达式成为函数声明。命名函数表达式的名字不会进入名字空间,也不会被提升。

f();//TypeError: f is not a function
foo();//ReferenceError: foo is not defined
var f = function foo(){console.log(typeof foo);};
f();//function
foo();//ReferenceError: foo is not defined

命名函数表达式的名字只在该函数的作用域内部有效。

文章来自 http://openwares.net/

· One min read

问题

js 中判断一个变量是否为对象,通常都是使用 typeof 来判断,那么还有其他办法么?

答案是当然的!

我们利用 Object 对象来实现这个功能。

原理

如果 Object 函数的参数是一个对象,它总是返回原对象。利用这一点,可以写一个判断变量是否为对象的函数。

方案

function isObject(value) {
return value === Object(value);
}

· One min read

NaN

定义

NaN 是 JavaScript 的特殊值,表示“非数字”(Not a Number),主要出现在将字符串解析成数字出错的场合。

注意

NaN 不是一种独立的数据类型,而是一种特殊数值,它的数据类型依然属于 Number,使用 typeof 运算符可以看得很清楚。

typeof NaN // 'number'

运算规则

NaN 不等于任何值,包括他本身

NaN === NaN // false

[NaN].indexOf(NaN) // -1

Boolean(NaN) // false

isNaN(NaN) // true
isNaN(123) // false

可靠的方式判断

利用它的特点,NaN 是 JavaScript 之中唯一不等于自身的值

function myIsNaN(value) {
return value !== value;
}

· One min read

引言

说到枚举,可能很多人都会想到枚举类型,但在 javascript 对象中有一个属性为可枚举性,他是什么呢?

概念

可枚举性(enumerable)用来控制所描述的属性,是否将被包括在 for...in 循环之中。具体来说,如果一个属性的 enumerable 为 false,下面三个操作不会取到该属性。

  • for..in 循环
  • Object.keys 方法
  • JSON.stringify 方法

enumerable “隐身术”

var o = {a:1, b:2};

o.c = 3;
Object.defineProperty(o, 'd', {
value: 4,
enumerable: false
});

o.d
// 4

for( var key in o ) console.log( o[key] );
// 1
// 2
// 3

Object.keys(o) // ["a", "b", "c"]

JSON.stringify(o // => "{a:1,b:2,c:3}"

上面代码中,d 属性的 enumerablefalse,所以一般的遍历操作都无法获取该属性,使得它有点像“秘密”属性,但还是可以直接获取它的值。

至于 for...in 循环和 Object.keys 方法的区别,在于前者包括对象继承自原型对象的属性,而后者只包括对象本身的属性。如果需要获取对象自身的所有属性,不管 enumerable 的值,可以使用 Object.getOwnPropertyNames 方法

· One min read

bind 方法,写 javascript 的肯定都见过,我也是,但是,不是经常用的话,基本上过一段时间就会模糊了,所以,决定把它转化成图像,估计比较容易记忆。

概念

我们来看看 MSDN 上关于 javascript bind 函数的解释:

对于给定函数,创建具有与原始函数相同的主体的绑定函数。 在绑定函数中,this 对象将解析为传入的对象。 绑定函数具有指定的初始参数。

哈哈哈哈,是不是懵逼了?

莫慌,我们慢慢来看,看看用法先:

用法

function.bind(thisArg[,arg1[,arg2[,argN]]])

参数

  • function 必选。 一个函数对象。
  • thisArg 必选。 this 关键字可在新函数中引用的对象。
  • arg1[,arg2[,argN]]] 可选。 要传递到新函数的参数的列表。

返回值

与 function 函数相同的新函数(注意是新函数!),thisArg 对象初始参数除外。

异常

如果指定的 function 不是函数,则将引发 TypeError 异常。

看到这里我们基本对 bind 方法的使用有个初步认识了,那先来看看具体示例再分析分析。

示例

this 绑定

/**
* 定义初始的函数
* 这个函数的功能很简单,就是判断数字是否在某个范围
*/
var checkNumericRange = function (value) {
if (typeof value !== 'number')
return false;
else
return value >= this.minimum && value <= this.maximum;
}

// 这里的范围将会被绑定到函数中的this值去
var range = { minimum: 10, maximum: 20 };

// 开始绑定!
var boundCheckNumericRange = checkNumericRange.bind(range);

// 使用一个数字来验证下这个函数
var result = boundCheckNumericRange (12);
document.write(result);

// 输出: true

好了,这个简单的示例看完了,我们知道了,使用 bind 将一个对象绑定到某个函数中,这个函数中所使用的 this 就会指向绑上去的函数了,不罗嗦了,画个图理解。 function 调用 bind 方法

再看个稍微有点不同的例子,其实也差不多:

// 创建一个带有刚才那个方法的对象,
// 并且方法调用当前这个对象中的最大值和最小值
var originalObject = {
minimum: 50,
maximum: 100,
checkNumericRange: function (value) {
if (typeof value !== 'number')
return false;
else
return value >= this.minimum && value <= this.maximum;
}
}

// 检查10是否在范围内
var result = originalObject.checkNumericRange(10);
document.write(result + " ");
// 输出: false

// 还是同样的配方,还是熟悉的味道
var range = { minimum: 10, maximum: 20 };

// bind技能要正在引导...
var boundObjectWithRange = originalObject.checkNumericRange.bind(range);

// 看看这次的效果
var result = boundObjectWithRange(10);
document.write(result);
// 输出: true, 有效了!

参数绑定

在参数中还可以有几个参数带进来 arg1[,arg2[,argN]]]

// 又是定义一个函数,这次是4个参数
var displayArgs = function (val1, val2, val3, val4) {
document.write(val1 + " " + val2 + " " + val3 + " " + val4);
}

var emptyObject = {};

// 使用bind,产生一个新函数
// 这个新函数的第一第二个参数已经定死了为这两个,再有参数往后排
var displayArgs2 = displayArgs.bind(emptyObject, 12, "a");

// 这里就是两个排队的参数了
displayArgs2("b", "c");
// 输出: 12 a b c

恩,知道,上图再说对吧 bind 函数绑定参数

上图可以看出 bind 时传入的参数,在新函数中作为最先使用的参数,但是它并没有改变原函数参数的个数

不知道看了两幅图,记住了 bind 方法没?

· One min read

new 操作符

加上 new 操作符,我们就能完成传统面向对象的 class + new 的方式创建对象,在 Javascript 中,我们将这类方式成为 Pseudoclassical。

我们执行如下代码:

var obj = new Base();

这样执行的结果是什么,javascript 引擎中看到的模型对象是: javascript 引擎中模型对象

new 操作符干了什么

new 操作符具体干了什么呢 ? 其实很简单,就干了三件事情。

var obj  = {};
obj.__proto__ = Base.prototype;
Base.call(obj);

第一行,我们创建了一个空对象 obj 第二行,我们将这个空对象的 __proto__ 成员指向了 Base 函数对象 prototype 成员对象 第三行,我们将 Base 函数对象的 this 指针替换成 obj,然后再调用 Base 函数,于是我们就给 obj 对象赋值了一个 id 成员变量,这个成员变量的值是”base”


2019-09-24 更新

最近看到一个专栏介绍 this 时,讲到为什么全局调用一个函数时,this 指向的是 window 对象呢? 因为全局调用时,并没有使用 call 方法绑定上下文,或是在一个对象中的方法调用 this ,而根据作用域链的规则,会从当前作用域范围向外找,找到全局作用域中的 window 对象。

而 new 操作时,实际上是调用了 call 方法将这个空对象设置为上下文指向的对象。

可以见 MDN 的解释 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/new

不过要注意是不是在严格模式下噢,行为不同

· One min read

起因

在开发时,只在 chrome 下开发,所以,在其他浏览器上的效果并未发现。 结果在 ff 和 IE 下的 new Date(str) 时,出现了 NaN。

比如如下代码:

var timestart = '2010-05-04';
var timeend = '2015-06-23';
var time1 = (timestart+' 00:00:00').toString();
var time2 = (timeend+' 23:59:59').toString();
timestart = new Date(time1);
timeend = new Date(time2);

尝试

  • IE 下的执行情况:

    结果:Invalid Date

  • firefox 下的执行情况:

    结果:Invalid Date

  • chrome 下的执行情况:

    结果:正常

正确的姿势

主要的变化是对默认的日期格式进行了转换, 基于 '/' 格式的日期字符串,才是被各个浏览器所广泛支持的,‘-’连接的日期字符串,则是只在 chrome 下可以正常工作。

var time1 = (timestart+' 00:00:00').toString();
var time2 = (timeend+' 23:59:59').toString();
timestart = new Date(Date.parse(str.replace(/-/g,"/"))).getTime();
timeend = new Date(Date.parse(str.replace(/-/g,"/"))).getTime();

结论

2015-06-23 是无法被各个浏览器中,使用 new Date(str) 来正确生成日期对象的。 正确的用法是 2015/06/23.

· One min read

什么是事件冒泡

JavaSciprt 事件中有两个很重要的特性:

  • 事件冒泡
  • 目标元素

在一个对象上触发某类事件(比如单击 onclick 事件),如果此对象定义了此事件的处理程序,那么此事件就会调用这个处理程序,如果没有定义此事件处理程序或者事件返回 true,那么这个事件会向这个对象的父级对象传播,从里到外,直至它被处理(父级对象所有同类事件都将被激活),或者它到达了对象层次的最顶层,即 document 对象(有些浏览器是 window)。

打个比方说:你在地方法院要上诉一件案子,如果地方没有处理此类案件的法院,地方相关部门会帮你继续往上级法院上诉,比如从市级到省级,直至到中央法院,最终使你的案件得以处理。

事件冒泡

事件冒泡允许多个操作被集中处理

事件冒泡允许多个操作被集中处理(把事件处理器添加到一个父级元素上,避免把事件处理器添加到多个子级元素上),它还可以让你在对象层的不同级别捕获事件

<div onclick="eventHandle(event)" id="outSide" >
<div id="inSide" ></div>
</div>
<script type="text/javascript">
//本例子只在外面盒子定义了处理方法,而这个方法一样可以捕获到子元素点击行为并处理它。假设有成千上万子元素要处理,难道我们要为每个元素加“onclick="eventHandle(event)"”?显然没有这种集中处理的方法来的简单,同时它的性能也是更高的。
function eventHandle(e)
{
var e=e||window.event;
var obj=e.target||e.srcElement;
alert(obj.id+' was click')
}
</script>

让不同的对象同时捕获同一事件

让不同的对象同时捕获同一事件 , 并调用自己的专属处理程序做自己的事情,就像老板一下命令,各自员工做自己岗位上的工作去了

<div onclick="outSideWork()" id="outSide" >
<div onclick="inSideWork()" id="inSide" ></div>
</div>
<script type="text/javascript">
function outSideWork()
{
alert('My name is outSide,I was working...');
}

function inSideWork()
{
alert('My name is inSide,I was working...');
}

//因为下面程序自动激活单击事件,有些浏览器不允许,所以请单击灰色盒子,从这里开始下命令,这样因为冒泡的原因,黑色大盒子也会收到单击事件,并调用了自己的处理程序。如果还有更多盒子嵌套,一样道理。

/*
function bossOrder()
{
document.getElmentById('inSide').click();
}
bossOrder();
*/
</script>

冒泡类型

冒泡型事件:事件按照从最特定的事件目标到最不特定的事件目标 (document 对象) 的顺序触发。

  • IE 5.5: div -> body -> document
  • IE 6.0: div -> body -> html -> document
  • Mozilla 1.0: div -> body -> html -> document -> window

阻止冒泡

通常情况下我们都是一步到位,明确自己的事件触发源,并不希望浏览器自作聪明、漫无目的地去帮我们找合适的事件处理程序,即我们明确最精准目标,这种情况下我们不需要事件冒泡。另外通过对事件冒泡的理解,我们知道程序将做多较多额外的事情,这必然增大程序开销。还有一个重要的问题是:事件冒泡处理可能会激活我们本来不想激活的事件,导致程序错乱,甚至无从下手调试,这常成为对事件冒泡不熟悉程序员的棘手问题。所以必要时,我们要阻止事件冒泡。

javascript 中兼容 IE 和 ff 的方式

//阻止事件冒泡函数
function stopBubble(e)
{
if (e && e.stopPropagation)
e.stopPropagation()
else
window.event.cancelBubble=true
}

阻止 jQuery 事件冒泡

jQuery 对 DOM 的事件触发具有冒泡特性。有时利用这一特性可以减少重复代码,但有时候我们又不希望事件冒泡。这个时候就要阻止 jQuery.Event 冒泡。

jQuery.Event 提供了一个非常简单的方法来阻止事件冒泡:event.stopPropagation();

$("p").click(function(event){
event.stopPropagation();
// do something
})

· One min read

创建节点

  • document.body 指向的是 body 元素
  • document.documentElement 则指向 html 元素
    //创建节点
var createNode = document.createElement("div");
var createTextNode = document.createTextNode("hello world");
createNode.appendChild(createTextNode);
document.body.appendChild(createNode);
document.documentElement.appendChild(createNode);

插入节点

可以使用

  • appendChild
  • insertBefore insertBefore 接收两个参数,第一个是插入的节点,第二个是参照节点,如 insertBefore(a,b),则 a 会插入在 b 的前面
    //插入节点
var createNode = document.createElement("div");
var createTextNode = document.createTextNode("hello world");
createNode.appendChild(createTextNode);
var div1 = document.getElementById("div1");
document.body.insertBefore(createNode,div1);

替换和删除元素

从 replaceChild 和 removeChild 的字面意思看,就是删除子节点,因此调用者,需要包含子节点 div1,不然调用会报错。返回的节点是替换的或删除的元素,被替换 / 删除的元素仍然存在,但 document 中已经没有他们的位置了。

//替换元素
var replaceChild = document.body.replaceChild(createNode,div1);
//删除元素
var removeChild = document.body.removeChild(div1);

节点的属性

  • firstChild: 第一个子节点
  • lastChild: 最后一个子节点
  • childNodes: 子节点集合,获取其中子节点可以 someNode.childNodes[index] 或者 someNode.childNodes.item(index)
  • nextSibling: 下一个兄弟节点
  • previousSibling:上一个兄弟节点
  • parentNode:父节点

文档片段

好处在于减少 dom 的渲染次数,可以优化性能。

//文本片段
var fragment = document.createDocumentFragment();
var ul = document.getElementById("ul");
var li = null;
for (var i = 4; i >= 0; i--) {
li = document.createElement("li");
li.appendChild(document.createTextNode("item "+i));
fragment.appendChild(li);
}
ul.appendChild(fragment);

克隆元素

  • someNode.cloneNode(true): 深度克隆,会复制节点及整个子节点
  • someNode.cloneNode(false): 浅克隆,会复制节点,但不复制子节点
//克隆
var clone = ul.cloneNode(true);
document.body.appendChild(clone);

注意

childNodes.length 存在跨浏览器的问题

可以看到有关列表的 html 片段没有用

<ul id="ul">
<li>sdsssssss</li>
<li>qqqq</li>
<li>wwww</li>
<li>eeee</li>
</ul>

这种书写格式而是使用没有换行的格式书写,是因为在不同的浏览器中,获取 ul.childNodes.length 的结果有差异: 在 ie 中,ul.childNodes.length 不会计算 li 之间的换行空格,从而得到数值为 4 在 ff、chrome,safari 中,会有包含 li 之间的空白符的 5 个文本节点,因此 ul.childNodes.length 为 9 若要解决跨浏览器问题,可以将 li 之间的换行去掉,改成一行书写格式。

cloneNode 存在跨浏览器的问题

在 IE 中,通过 cloneNode 方法复制的元素,会复制事件处理程序,比如,var b = a.cloneNode(true). 若 a 存在 click,mouseover 等事件监听,则 b 也会拥有这些事件监听。 在 ff,chrome,safari 中,通过 cloneNode 方法复制的元素,只会复制特性,其他一切都不会复制 因此,若要解决跨浏览器问题,在复制前,最好先移除事件处理程序。

· One min read

大家都知道事件的用法就是当某个事件 (状况) 被触发了之后就会去执行某个 Function, 尤其是 Javascript, 在当红 AJAX 的催化下 , 了解 Javascript 的 Event 用法更加重要 , 在这里就大概介绍一下 avascript 的 Event 用法。

添加事件

Mozilla 中

addEventListener 的使用方式:

target.addEventListener(type, listener, useCapture);

  • target: 文档节点、document、window 或 XMLHttpRequest。
  • type: 字符串,事件名称,不含“on”,比如“click”、“mouseover”、“keydown”等。
  • listener :实现了 EventListener 接口或者是 JavaScript 中的函数。
  • useCapture :是否使用捕捉,一般用 false 。例如:document.getElementById("testText").addEventListener("keydown", function (event) { alert(event.keyCode); }, false);

栗子

document.getElementById("testText").addEventListener("keydown", function (event) { alert(event.keyCode); }, false);

IE中

target.attachEvent(type, listener);

  • target: 文档节点、document、window 或 XMLHttpRequest。
  • type: 字符串,事件名称,含“on”,比如“onclick”、“onmouseover”、“onkeydown”等。
  • listener :实现了 EventListener 接口或者是 JavaScript 中的函数。 例如:document.getElementById("txt").attachEvent("onclick",function(event){alert(event.keyCode);});

移除事件

W3C 及 IE 同时支持移除指定的事件 , 用途是移除设定的事件 , 格式分别如下 :

W3C 格式

removeEventListener(event,function,capture/bubble);

Windows IE的格式如下

detachEvent(event,function);

浏览器兼容

适应的浏览器版本不同,同时在使用的过程中要注意

  • attachEvent 方法 按钮 onclick IE 中使用
  • addEventListener 方法 按钮 click fox 中使用

两者使用的原理:可对执行的优先级不一样:

  • attachEvent 方法,为某一事件附加其它的处理事件。(不支持 Mozilla 系列)
  • addEventListener 方法 用于 Mozilla 系列

栗子

document.getElementById("btn").onclick = method1;
document.getElementById("btn").onclick = method2;
document.getElementById("btn").onclick = method3;

如果这样写 , 那么将会只有 medhot3 被执行

写成这样:

var btn1Obj = document.getElementById("btn1"); //object.attachEvent(event,function);
btn1Obj.attachEvent("onclick",method1);
btn1Obj.attachEvent("onclick",method2);
btn1Obj.attachEvent("onclick",method3);

执行顺序为 method3->method2->method1

如果是 Mozilla 系列,并不支持该方法,需要用到 addEventListener

var btn1Obj = document.getElementById("btn1");
//element.addEventListener(type,listener,useCapture);
btn1Obj.addEventListener("click",method1,false);
btn1Obj.addEventListener("click",method2,false);
btn1Obj.addEventListener("click",method3,false);

执行顺序为 method1->method2->method3

兼容 IE 和 firefox 的事件处理

封装

function addListener(element, eventName, handler) {
if (element.addEventListener) {
element.addEventListener(eventName, handler, false);
}
else if (element.attachEvent) {
element.attachEvent('on' + eventName, handler);
}
else {
element['on' + eventName] = handler;
}
}

function removeListener(element, eventName, handler) {
if (element.addEventListener) {
element.removeEventListener(eventName, handler, false);
}
else if (element.detachEvent) {
element.detachEvent('on' + eventName, handler);
}
else {
element['on' + eventName] = null;
}
}

上面函数有两处需要注意一下就是 :

  1. 第一个分支最好先测定 w3c 标准 . 因为 IE 也渐渐向标准靠近 . 第二个分支监测 IE.
  2. 第三个分支是留给既不支持 (add/remove)EventListener 也不支持 (attach/detach)Event 的浏览器 .

优化

对于上面的函数我们是运用 " 运行时 " 监测的 . 也就是每次绑定事件都需要进行分支监测 . 我们可以将其改为 " 运行前 " 就确定兼容函数 . 而不需要每次监测 .

这样我们就需要用一个 DOM 元素提前进行探测 . 这里我们选用了 document.documentElement. 为什么不用 document.body 呢 ? 因为 document.documentElement 在 document 没有 ready 的时候就已经存在. 而 document.body 没 ready 前是不存在的 .

这样函数就优化成

var addListener, removeListener,
/* test element */
docEl = document.documentElement;

// addListener
if (docEl.addEventListener) {
/* if `addEventListener` exists on test element, define function to use `addEventListener` */
addListener = function (element, eventName, handler) {
element.addEventListener(eventName, handler, false);
};
} else if (docEl.attachEvent) {
/* if `attachEvent` exists on test element, define function to use `attachEvent` */
addListener = function (element, eventName, handler) {
element.attachEvent('on' + eventName, handler);
};
} else {
/* if neither methods exists on test element, define function to fallback strategy */
addListener = function (element, eventName, handler) {
element['on' + eventName] = handler;
};
}

// removeListener
if (docEl.removeEventListener) {
removeListener = function (element, eventName, handler) {
element.removeEventListener(eventName, handler, false);
};
} else if (docEl.detachEvent) {
removeListener = function (element, eventName, handler) {
element.detachEvent('on' + eventName, handler);
};
} else {
removeListener = function (element, eventName, handler) {
element['on' + eventName] = null;
};
}

· One min read

segmentfault 上看到的一个问直接在文本框中粘贴就可以上传图片的例子。

/** 粘贴上传图片 **/
document.body.addEventListener('paste', function(e) {
var clipboard = e.clipboardData;
for(var i=0,len=clipboard.items.length; i<len; i++) {
if(clipboard.items[i].kind == 'file' || clipboard.items[i].type.indexOf('image') > -1) {
var imageFile = clipboard.items[i].getAsFile();
var form = new FormData;
form.append('image', imageFile);
$.ajax({
url: channel.server+"/u.php?community="+channel.community,
type: "POST",
data: form,
processData: false,
contentType: false,
beforeSend: function() {
$('#uploadmessage').html('正在上传图片...');
},
error: function() {
$('#uploadmessage').html('上传失败请重新上传!');
setTimeout('$(\'#uploadmessage\').html(\'\')', 1000);
},
success: function(url) {
$('#uploadmessage').html('图片上传成功');
setTimeout('$(\'#uploadmessage\').html(\'\')', 1000);
var textarea = $('.textarea');
textarea.val(textarea.val() + '[![]('+url+')]('+url+')')
}
})
e.preventDefault();
}
}
});

这里是帖子原文

http://segmentfault.com/q/1010000002729267

· One min read

在 IE9 以下是不支持 JSON.parse 方法来解析 JSON 字符串的。

原来,在 IE8 中使用原生 JSON 对象是有条件的。微软在介绍 JSON 对象时也有下面的一段注释:

Starting with JScript. 5.8, by default, the JScript. scripting engine supports the language feature set as it existed in version 5.7. This is to maintain compatibility with the earlier versions of the engine. To use the complete language feature set of version 5.8, the Windows Script. interface host has to invoke IActiveScriptProperty::SetProperty.

Internet Explorer 8 opts into the JScript. 5.8 language features when the document mode for Internet Explorer 8 is "Internet Explorer 8 Standards" mode. For other document modes, Internet Explorer uses the version 5.7 feature set.

JScript. 5.8 includes native JavaScript. Object Notation (JSON) support and the accessor methods for Document Object Model (DOM) prototypes.

由于 JSON 对象是在 JScript. 5.8 及其以后的版本引入的,所以,默认情况下,IE8 使用的是 JScript. 5.7 版本,所以,原生 JSON 对象是无法使用的。

1.eval 方式解析,恐怕这是最早的解析方式了

    function strToJson(str){
var json = eval('(' + str + ')');
return json;
}

但是出于安全性的考虑,建议尽量不要使用 eval,如果从第三方获取数据进行解析,会存在恶意脚本代码的风险。

2.new Function 形式,比较怪异。

    function strToJson(str){
var json = (new Function("return " + str))();
return json;
}

· One min read

作者 崔康

异步模式在 web 编程中变得越来越重要,对于 web 主流语言 Javascript 来说,这种模式实现起来不是很利索,为此,许多 Javascript 库(比如 jQuery 和 Dojo)添加了一种称为 promise 的抽象(有时也称之为 deferred)。通过这些库,开发人员能够在实际编程中使用 promise 模式。IE 官方博客最近发表了一篇 文章,详细讲述了如何使用 XMLHttpRequest2 来实践 promise 模式。我们来了解一下相关的概念和应用。

考虑这样一个例子,某网页存在异步操作(通过 XMLHttpRequest2 或者 WebWorkers)。随着 Web 2.0 技术的深入,浏览器端承受了越来越多的计算压力,所以“并发”具有积极的意义。对于开发人员来说,既要保持页面与用户的交互不受影响,又要协调页面与异步任务的关系,这种非线性执行的编程要求存在适应的困难。先抛开页面交互不谈,我们能够想到对于异步调用需要处理两种结果——成功操作和失败处理。在成功的调用后,我们可能需要把返回的结果用在另一个 Ajax 请求中,这就会出现“函数连环套”的情况(在笔者的另一篇文章《NodeJS 的异步编程风格》中有详细的解释)。这种情况会造成编程的复杂性。看看下面的代码示例(基于 XMLHttpRequest2):

    function searchTwitter(term, onload, onerror) {

var xhr, results, url;
url = 'http://search.twitter.com/search.json?rpp=100&amp;q=' + term;
xhr = new XMLHttpRequest();
xhr.open('GET', url, true);

xhr.onload = function (e) {
if (this.status === 200) {
results = JSON.parse(this.responseText);
onload(results);
}
};

xhr.onerror = function (e) {
onerror(e);
};

xhr.send();
}

function handleError(error) {
/* handle the error */
}

function concatResults() {
/* order tweets by date */
}

function loadTweets() {
var container = document.getElementById('container');

searchTwitter('#IE10', function (data1) {
searchTwitter('#IE9', function (data2) {
/* Reshuffle due to date */
var totalResults = concatResults(data1.results, data2.results);
totalResults.forEach(function (tweet) {
var el = document.createElement('li');
el.innerText = tweet.text;
container.appendChild(el);
});
}, handleError);
}, handleError);
}

上面的代码其功能是获取 Twitter 中 hashtag 为 IE10 和 IE9 的内容并在页面中显示出来。这种嵌套的回调函数难以理解,开发人员需要仔细分析哪些代码用于应用的业务逻辑,而哪些代码处理异步函数调用的,代码结构支离破碎。错误处理也分解了,我们需要在各个地方检测错误的发生并作出相应的处理。

为了降低异步编程的复杂性,开发人员一直寻找简便的方法来处理异步操作。其中一种处理模式称为 promise,它代表了一种可能会长时间运行而且不一定必须完整的操作的结果。这种模式不会阻塞和等待长时间的操作完成,而是返回一个代表了承诺的(promised)结果的对象。

考虑这样一个例子,页面代码需要访问第三方的 API,网络延迟可能会造成响应时间较长,在这种情况下,采用异步编程不会影响整个页面与用户的交互。promise 模式通常会实现一种称为 then 的方法,用来注册状态变化时对应的回调函数。比如下面的代码示例:

    searchTwitter(term).then(filterResults).then(displayResults);

promise 模式在任何时刻都处于以下三种状态之一:未完成(unfulfilled)、已完成(resolved)和拒绝(rejected)。以 CommonJS Promise/A 标准为例,promise 对象上的 then 方法负责添加针对已完成和拒绝状态下的处理函数。then 方法会返回另一个 promise 对象,以便于形成 promise 管道,这种返回 promise 对象的方式能够支持开发人员把异步操作串联起来,如 then(resolvedHandler, rejectedHandler); 。resolvedHandler 回调函数在 promise 对象进入完成状态时会触发,并传递结果;rejectedHandler 函数会在拒绝状态下调用。

有了 promise 模式,我们可以重新实现上面的 Twitter 示例。为了更好的理解实现方法,我们尝试着从零开始构建一个 promise 模式的框架。首先需要一些对象来存储 promise。

    var Promise = function () {
/* initialize promise */
};

接下来,定义 then 方法,接受两个参数用于处理完成和拒绝状态。

Promise.prototype.then = function (onResolved, onRejected) {
/* invoke handlers based upon state transition */
};

同时还需要两个方法来执行理从未完成到已完成和从未完成到拒绝的状态转变。

    Promise.prototype.resolve = function (value) {
/* move from unfulfilled to resolved */
};

Promise.prototype.reject = function (error) {
/* move from unfulfilled to rejected */
};

现在搭建了一个promise的架子,我们可以继续上面的示例,假设只获取IE10的内容。创建一个方法来发送Ajax请求并将其封装在promise中。这个promise对象分别在xhr.onload和xhr.onerror中指定了完成和拒绝状态的转变过程,请注意searchTwitter函数返回的正是promise对象。然后,在loadTweets中,使用then方法设置完成和拒绝状态对应的回调函数。

    function searchTwitter(term) {

var url, xhr, results, promise;
url = 'http://search.twitter.com/search.json?rpp=100&amp;q=' + term;
promise = new Promise();
xhr = new XMLHttpRequest();
xhr.open('GET', url, true);

xhr.onload = function (e) {
if (this.status === 200) {
results = JSON.parse(this.responseText);
promise.resolve(results);
}
};

xhr.onerror = function (e) {
promise.reject(e);
};

xhr.send();
return promise;
}

function loadTweets() {
var container = document.getElementById('container');
searchTwitter('#IE10').then(function (data) {
data.results.forEach(function (tweet) {
var el = document.createElement('li');
el.innerText = tweet.text;
container.appendChild(el);
});
}, handleError);
}

到目前为止,我们可以把promise模式应用于单个Ajax请求,似乎还体现不出promise的优势来。下面来看看多个Ajax请求的并发协作。此时,我们需要另一个方法when来存储准备调用的promise对象。一旦某个promise从未完成状态转化为完成或者拒绝状态,then方法里对应的处理函数就会被调用。when方法在需要等待所有操作都完成的时候至关重要。

    Promise.when = function () {
/* handle promises arguments and queue each */
};

以刚才获取IE10和IE9两块内容的场景为例,我们可以这样来写代码:

    var container, promise1, promise2;
container = document.getElementById('container');
promise1 = searchTwitter('#IE10');
promise2 = searchTwitter('#IE9');
Promise.when(promise1, promise2).then(function (data1, data2) {

/* Reshuffle due to date */
var totalResults = concatResults(data1.results, data2.results);
totalResults.forEach(function (tweet) {
var el = document.createElement('li');
el.innerText = tweet.text;
container.appendChild(el);
});
}, handleError);

分析上面的代码可知,when函数会等待两个promise对象的状态发生变化再做具体的处理。在实际的Promise库中,when函数有很多变种,比如 when.some()、when.all()、when.any()等,读者从函数名字中大概能猜出几分意思来,详细的说明可以参考CommonJS的一个promise实现when.js

除了CommonJS,其他主流的Javascript框架如jQuery、Dojo等都存在自己的promise实现。开发人员应该好好利用这种模式来降低异步编程的复杂性。我们选取Dojo为例,看一看它的实现有什么异同。

Dojo框架里实现promise模式的对象是Deferred,该对象也有then函数用于处理完成和拒绝状态并支持串联,同时还有resolve和reject,功能如之前所述。下面的代码完成了Twitter的场景:

    function searchTwitter(term) {

var url, xhr, results, def;
url = 'http://search.twitter.com/search.json?rpp=100&amp;q=' + term;
def = new dojo.Deferred();
xhr = new XMLHttpRequest();
xhr.open('GET', url, true);

xhr.onload = function (e) {
if (this.status === 200) {
results = JSON.parse(this.responseText);
def.resolve(results);
}
};

xhr.onerror = function (e) {
def.reject(e);
};

xhr.send();
return def;
}

dojo.ready(function () {
var container = dojo.byId('container');
searchTwitter('#IE10').then(function (data) {
data.results.forEach(function (tweet) {
dojo.create('li', {
innerHTML: tweet.text
}, container);
});
});
});

不仅如此,类似dojo.xhrGet方法返回的即是dojo.Deferred对象,所以无须自己包装promise模式。

   var deferred = dojo.xhrGet({
url: "search.json",
handleAs: "json"
});

deferred.then(function (data) {
/* handle results */
}, function (error) {
/* handle error */
});

除此之外,Dojo 还引入了 dojo.DeferredList, 支持开发人员同时处理多个 dojo.Deferred 对象,这其实就是上面所提到的 when 方法的另一种表现形式。

    dojo.require("dojo.DeferredList");
dojo.ready(function () {
var container, def1, def2, defs;
container = dojo.byId('container');
def1 = searchTwitter('#IE10');
def2 = searchTwitter('#IE9');

defs = new dojo.DeferredList([def1, def2]);

defs.then(function (data) {
// Handle exceptions
if (!results[0][0] || !results[1][0]) {
dojo.create("li", {
innerHTML: 'an error occurred'
}, container);
return;
}
var totalResults = concatResults(data[0][1].results, data[1][1].results);

totalResults.forEach(function (tweet) {
dojo.create("li", {
innerHTML: tweet.text
}, container);
});
});
});

上面的代码比较清楚,不再详述。

说到这里,读者可能已经对 promise 模式有了一个比较完整的了解,异步编程会变得越来越重要,在这种情况下,我们需要找到办法来降低复杂度,promise 模式就是一个很好的例子,它的风格比较人性化,而且主流的 JS 框架提供了自己的实现。所以在编程实践中,开发人员应该尝试这种便捷的编程技巧。需要注意的是,promise 模式的使用需要恰当地设置 promise 对象,在对应的事件中调用状态转换函数,并且在最后返回 promise 对象。

技术社区对异步编程的关注也在升温,国内社区也发出了自己的声音。资深技术专家老赵就发布了一套开源的异步开发辅助库 Jscex,它的设计很巧妙,抛弃了回调函数的编程方式,采用一种“线性编码、异步执行”的思想,感兴趣的读者可以查看 这里

不仅仅是前端的 JS 库,如今火热的 NodeJS 平台也出现了许多第三方的 promise 模块,具体的清单可以访问 这里

注:本文中的所有代码示例均来自于 IE 官方博客。

原文来自 InfoQ :http://www.infoq.com/cn/news/2011/09/js-promise/

· One min read

这里分享下怎么样将时间显示的更加人性化。

通常我们在页面上显示日期时间的时候,通常是 yyyy-MM-dd HH:mm:ss,也就是年月日时分秒的格式,这样确实比较规范,但是这样的时间在社交类或博客类网站中是否有好处呢?

答案也不是一定的,也许有人说我就喜欢这样的格式!

但是,在数据爆发的这个时代,我们总会想看一些最新的消息,比如:

当我们百度搜某个新闻的时候,我们通常会在结果中查找比较新的,后面的人性化时间会给我们指导作用。

举个例子:

2014 年感动中国十大人物 刚刚

2014 年感动中国十大人物出炉 3 分钟前

看看 2014 年感动中国十大人物 2014-12-27 12:28

这几个结果中,相信一般的老百姓大都会从第一个开始点吧,不多说废话了,看看 Javascript 代码如何实现的:

function hommizationTime(dateTimeStamp){
var result;
var minute = 1000 * 60;
var hour = minute * 60;
var day = hour * 24;
var month = day * 30;
var now = new Date().getTime();
var diffValue = now - dateTimeStamp;
if(diffValue &lt; 0){
// 非法操作
//alert(" 结束日期不能小于开始日期!");
}
var monthC =diffValue/month;
var weekC =diffValue/(7*day);
var dayC =diffValue/day;
var hourC =diffValue/hour;
var minC =diffValue/minute;

if(monthC&gt;=1){
result="" + parseInt(monthC) + " 个月前发表 ";
}
else if(weekC&gt;=1){
result="" + parseInt(weekC) + " 个星期前发表 ";
}
else if(dayC&gt;=1){
result=""+ parseInt(dayC) +" 天前发表 ";
}
else if(hourC&gt;=1){
result=""+ parseInt(hourC) +" 个小时前发表 ";
}
else if(minC&gt;=1){
result=""+ parseInt(minC) +" 分钟前发表 ";
}else
result=" 刚刚发表 ";
return result;
}

原理就是根据不同的时间单位用当前和发表时的时间差来相除,看,是不是 so easy!