UMD兼容AMD和CMD的写法

UMD 规范 http://davidbcalhoun.com/2014/what-is-amd-commonjs-and-umd/

同时支持 AMD 和 CMD, 做一层封装即可

这里使用 jquery 和 underscore 做例子:

(function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD 格式
        define(['jquery', 'underscore'], factory);
    } else if (typeof exports === 'object') {
        // CMD 格式
        module.exports = factory(require('jquery'), require('underscore'));
    } else {
        挂载到浏览器 window 下
        root.returnExports = factory(root.jQuery, root._);
    }
}(this, function ($, _) {
    //    定义几个方法
    function a{};    //    私有方法, 不暴露出去
    function b{};    //    公有方法
    function c{};    //    公有方法

    // 将公有方法暴露出去
    return {
        b: b,
        c: c
    }
}));

JavaScript 碎片

  • Object.defineProperty

      var A = {};
      Object.defineProperty(A, 'attrName', {
          set: function(val) { 
              this.__attrName__ = val; 
              console.log('A.attrName 被设置为: ', val);
          },
          get: function() { 
              console.log('A.attrName 被获取到: ', this.__attrName__);
              return this.__attrName__; }
          });
      });    
    

阻止元素被选中及清除选中的方法

  1. 1. 阻止选中
    1. 1.1. 使用 JS 阻止整个网页的内容被选中
    2. 1.2. 阻止特定区域的内容被选中
    3. 1.3. 使用 CSS 控制样式阻止内容被选中
  2. 2. 清除选中
    1. 2.1. 使用 JS 清除选中
    2. 2.2. 使用 CSS 清除选中
      1. 2.2.1. 参考链接

有时候,我们希望阻止用户选中我们指定区域的文字或内容。

举个栗子,有时候用户在一个区域执行频繁的点击操作,一不小心傲娇地点多了,就会选中当前区域的内容。

再举个栗子,制作轮播组件的时候,点击下一页,若点击的快的话,浏览器会识别为双击。

双击的默认效果是选中整片区域,这时候轮播图组件就会被表示忧郁的蓝色幕布盖住,多忧桑啊~

你看,这妹子多赞啊,可是你一紧张就乱点下一张的话,就变成酱紫了:

不过别怕,给这个现代化浏览器说好了咱不要这种忧桑色调就可以了:

1
2
3
.pretty-girl {
-webkit-user-select: none;
}

可是!可是!不是每个浏览器都可以不忧桑!!!那就只能请脚本大王出山了。

阻止选中

有时候,我们需要禁止用户选中一些文本区域,这时候可以直接通过让 onselectstart 事件 return false 来实现。

使用 JS 阻止整个网页的内容被选中

1
2
3
4
5
6
7
8
document.body.onselectstart = function () {
return false;
};
// 或
document.body.onmousedown = function () {
return false;
}

阻止特定区域的内容被选中

1
2
3
4
var elem = document.getElementById('elemId');
elem.onselectstart = function () {
return false;
};

使用 CSS 控制样式阻止内容被选中

仅支持非 IE10 以下的浏览器。IE9 以下请使用 onselectstart=”return false;” 的方式来实现。

1
2
3
4
5
6
7
8
9
10
.unselect {
-webkit-user-select: none;
-moz-user-select: none;
-khtml-user-select: none;
-ms-user-select: none;
/* 以下两个属性目前并未支持,写在这里为了减少风险 */
-o-user-select: none;
user-select: none;
}

user-select: auto; => 用户可以选中元素中的内容

user-select: none; => 用户不可选中元素中的内容

user-select: text; => 用户可以选中元素中的文字

目前这个 user-select 兼容 Chrome 6+、Firefox、IE 10+、Opera 15+、Safari 3.1+。

需要注意的是,这个 user-select 还带浏览器厂商前缀,意味着她们还是非标准的,将来可能会改变。在生产环境中要慎用。

清除选中

有时候用户选中文字进行复制后,我们使用手动的方式进行选中的清除。

使用 JS 清除选中

1
2
3
4
5
6
7
8
9
10
11
12
13
function clearSelections () {
if (window.getSelector) {
// 获取选中
var selection = window.getSelection();
// 清除选中
selection.removeAllRanges();
} else if (document.selection && document.selection.empty) {
// 兼容 IE8 以下,但 IE9+ 以上同样可用
document.selection.empty();
// 或使用 clear() 方法
// document.selection.clear();
}
}

使用 CSS 清除选中

不考虑低版本 IE 的情况下,我们简单给选中元素添加以上 .unselect 的样式即可。

参考链接

MDN user-select

Base 64 编码

在浏览器中,使用 window.btoa() 将字符串或二进制值转化为 Base64 编码,使用 window.atob() 还原。

1
2
window.btoa('laispace'); //=> "bGFpc3BhY2U="
window.atob("bGFpc3BhY2U="); //=>"laispace"

注意,要将非 ASCII 编码字符转化为 Base64 编码的话,需要先进行转码,否则会报错

1
2
3
window.btoa(encodeURI('赖小赖')); //=> "JUU4JUI1JTk2JUU1JUIwJThGJUU4JUI1JTk2"
window.atob('JUU4JUI1JTk2JUU1JUIwJThGJUU4JUI1JTk2'); //=> "%E8%B5%96%E5%B0%8F%E8%B5%96"
decodeURI("%E8%B5%96%E5%B0%8F%E8%B5%96"); //=> "赖小赖"

XMLHttpRequest 对象新特性

旧版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 新建实例
var xhr = new XMLHttpRequest();
// 向服务器发出请求
xhr.open('GET','example.php');
xhr.send();
// 监控xhr对象的状态变化,制定回调函数
xhr.onreadystatechange = function(){
if(xhr.readyState ==4 && xhr.status ==200){ // 4表示数据接收完毕,200表示服务器返回一切正常
alert( xhr.responseText );
} else {
alert( xhr.statusText );
}
}

阅读全文

图片预加载和判断图片是否加载完成

使用Image()对象预加载图片:

var imgsSrcArray = [src1, src2, src3, src4, src5];
需要预加载这些图片,以便点击上一张或下一张按钮的时候,能显示对应的图片。
使用JS自带的 Image对象 实现图片预加载:
[javascript]
var images= [];
var len = imgsSrcArray.length;
for(var i = 0; i < len; i++) {
var img = new Image();
img.src = imgsScrArray[i];
images.push(img)
}
[/javascript]

这样就实现了将多个图片进行了预加载,缓存到了images数组里。

使用img.onload,当图片加载完成时执行操作:

[javascript]
img.onload = function(){
// console.log(‘图片加载完成’)
};
[/javascript]

判断图片是否已经加载完成则:需要判断 img.complete属性是否为true:

if (img.complete) {
// 执行操作
}

优化刚刚的预加载函数,封装一个预加载非固定数目图片的函数

[javascript]
var images = [];
function preloadImgs() {
// 利用arguments参数判断需要加载的图片数目
var len = preloadImgs.arguments.length;
for (var i=0; i<len; i++) {
images[i] = new Image();
images[i].src = preloadImgs.arguments[i];
}
}
[/javascript]

// 或传入数组
[javascript]
var images = [];
function preloadImgs(srcArray) {
// 利用arguments参数判断需要加载的图片数目
var len = preloadImgs.arguments.length;
for (var i=0; i<len; i++) {
images[i] = new Image();
images[i].src = srcArray[i];
}
}
[/javascript]

阅读全文

Cookies学习笔记

Cookie 的格式是:

[javascript]
name=<value>[; expires=<date>][; domain=<domain>][; path=<path>][; secure]
//名称=<值>[; expires=<日期>][; domain=<域>][; path=<路径>][; 安全]
[/javascript]

// 设置cookie

[javascript]
document.cookie="key=escape(value)";
[/javascript]

//escape()函数进行编码,它能将一些特殊符号使用十六进制表示,例如空格将会编码为“20%”,从而可以存储于cookie值中,而且使用此 种方案还可以避免中文乱码的出现。在取值的时候需要unescape(value)对value再进行转码即可。

// 设置多个cookie
设置多个cookie需要多次使用这样的方法。正确的设置方法是:

[javascript]
document.cookie="key=escape(value)";
document.cookie="key1=escape(value1)"
// 而不是
document.cookie="key=escape(value);key1=escape(value1)";
[/javascript]
// 获取cookie,注意第二个开始key值前面有空格:

[javascript]
function getCookie(key){
var aCookie = document.cookie.split(";");
for (var i=0; i < aCookie.length; i++){
var aCrumb = aCookie[i].split("=");
if (key === aCrumb[0].replace(/^\s|\s$/,"")){
return unescape(aCrumb[1]);
}
}
}
[/javascript]
// 设置cookie的存活时间:

[javascript]
var liveDate = new Date();
liveDate.setTime(liveDate.getTime() + 32460601000); //设置cookie的name的存活时间为3天。
document.cookie="name=test;expires=" + liveDate.toGMTString();
[/javascript]
// 删除cookie,设置expires一个过期的时间即可

[javascript]
var liveDate = new Date();
liveDate.setTime(liveDate.getTime() - 10000);
document.cookie = "name=test;expires=" + date.toGMTString();
[/javascript]
// 拓展cookie的作用域到根目录:

[javascript]
document.cookie="key=escape(value);path=/";
[/javascript]
// 设置cookie的访问域

[javascript]
document.cookie="name=value;domain=cookieDomain";
//以Laispace为例,要实现跨主机访问,可以写为:
document.cookie="name=value;domain=.laispace.com"; //所有Laispace.com下的主机都可以访问该cookie
[/javascript]
// 设置cookie的访问权限
设置了该属性,只有使用https协议才能够访问到
注意点:
如果设置cookie时带path属性,那么在删除的时候一定要加上path属性,否则删除的是当前目录下设置的cookie值。

Javascript 闭包的学习

名词定义:包裹一些局部变量的一个函数叫做一个闭包。闭包是个函数,而它「记住了周围发生了什么」。表现为由「一个函数」体中定义了「另个函数」
实现原理:嵌套函数可以访问外部作用域中声明的变量。
组成结构:函数以及构建这个函数的环境。
使用价值:将函数与其所操作的某些数据(环境)关连起来
使用缺点:闭包会影响性能!闭包会使得函数中的变量都被保存在内存中,内存消耗很大。

  • 用途1,将数据与多个函数相关联:
    [javascript]
    function makeSizer(size) {
    return function() {
    document.body.style.fontSize = size + ‘px’;
    };
    }

    var size12 = makeSizer(12);
    var size14 = makeSizer(14);
    var size16 = makeSizer(16);

    //size12(); // 将字号调整到12px
    //size14(); // 将字号调整到14px
    //size16(); // 将字号调整到16px
    [/javascript]

  • 用途2,模拟私有方法:
    私有方法不仅仅有利于限制对代码的访问:还提供了管理全局命名空间的强大能力,避免非核心的方法弄乱了代码的公共接口部分。
    [javascript]
    // 创建一个环境为三个函数共享,减少了污染
    var Counter = (function() {
    var privateCounter = 0;
    function changeBy(val) {
    privateCounter += val;
    }
    return {
    increment: function() {

     changeBy(1);
    

    },
    decrement: function() {

     changeBy(-1);
    

    },
    value: function() {

     return privateCounter;
    

    }
    }
    })();

    alert(Counter.value()); / 提示 0 /
    Counter.increment();
    Counter.increment();
    alert(Counter.value()); / 提示 2 /
    Counter.decrement();
    alert(Counter.value()); / 提示 1 /
    [/javascript]

这种方法跟创建一个对象,并分别定义对象的三个方法相似吧?
实验结果显示,这两种方式是相同的,不过小赖觉得,以下这种更为直观一点吧:
[javascript]
// 创建一个环境为三个函数共享,减少了污染
var Counter = {
privateCounter : 0,

increment: function() {
this.privateCounter +=1;
},

 decrement: function() {
   this.privateCounter -=1;
 },

 value: function() {
   return this.privateCounter;
 }

}

alert(Counter.value()); / 提示 0 /
Counter.increment();
Counter.increment();
alert(Counter.value()); / 提示 2 /
Counter.decrement();
alert(Counter.value()); / 提示 1 /
[/javascript]

使用闭包的话,三个方法共享一个环境,而使用对象来创建明明空间,实则是通过this指向这个对象,来保证共享一个环境,也是避免了污染,有异曲同工之妙吧。

参考:
1. 闭包
2. 闭包
3. secrets_of_javascript_closures

Javascript 最佳实践

  1. 1. 编码技巧
  2. 2. 新鲜概念

// @author lxl:使用高质量JS代码对提高性能肯定是非常有帮助的,小赖对常见的好方法总结在这里,不断更新。
// @update 2013/10/04
-
使用压缩后的文件(如lxl.min.js),并开启http gzip压缩工具
-
尽量将script标签放在前,可以尝试用异步加载的方法加载js文件
-
尽量保证js代码和HTML结构的分离,不要内嵌代码到HTML中,以统一维护和缓存处理

-

  • 避免全局变量的污染

1. 使用命名空间
2. 匿名函数封装立即执行
3. 始终使用var来声明变量

  • 使用var声明(显式)的全局变量是不能被删除的
  • 未用var声明(隐式)的全局变量是可以被删除的
  • 隐式全局变量并非真正的全局变量,但却是全局对象的属性,因为属性是可以通过delete操作符删除的,而变量则不行

  • for循环中将长度缓存到变量中。

1
避免重复计算HTMLCollections的长度(操作DOM一般都是比较昂贵的,缓存长度后效率竟然是是IE7下不缓存的190倍!)
  • for循环中建议使用降序来遍历
1
向下数到0通常更快,因为和0作比较要比和数组长度或非0的东西作比较更有效率
  • for-in循环只在遍历对象属性的时候才使用,其他情况建议使用for循环就够了
1
for-in 循环枚举出的顺序是不能保证的,且若数组对象已被自定义的功能增强,就可能发生逻辑错误
  • 避免隐式类型转换,使用===或!==总是最严谨的!
1
这总能避免一些意想不到的类型转换问题,不是么?
  • 使用hasOwnProperty()方法过滤从原型链继承的属性,如:
1
2
3
4
5
for (var i in man) {
if (man.hasOwnProperty(i)) { // 过滤
console.log(i, ":", man[i]);
}
}
  • 使用单var语句声明变量
1
2
3
4
变量的声明会被被JS引擎提至函数顶部预解析(hoisting),不如直接使用单var统 一声明所有将会用到的变量,方便查询又易于管理,如:
var a = 1,
b = 2,
c = 3;
  • 避免改变或增加原型对象的方法
1
2
3
4
5
6
7
随意改变或增加原型会增加维护成本,当以后使用一个方法却发现这个方法被重定义时,就会带来问题。
除非团队认可这种做法并意识到原型已添加了方法,知道怎么使用时:
if( typeof Object.prototype.myMethod !== "function"){
Object.prototype.myMethod = function(){
// 实现新增方法
}
}
  • 避免使用eval语句
1
2
3
4
5
6
7
8
9
10
eval是魔鬼,除非知道它执行的代码本身会有什么问题。
eval里的代码被恶意篡改的话,就会带来严重的安全问题。
若绝对需要使用eval,实则可以
1.new Function()替代,因为它有局部函数作用域,其中的var变量不会变成全局 的,可以避免一些问题.
2.将eval语句封装到即时的匿名函数中,与1有相同效果。
注意,setIntervalsetTimeout中传递字符串跟eval()是一样的,要注意避免直接 传递字符串:
setTimeout(myFunc, 1000); // 正确
setTimeout(function () { // 正确
myFunc(1, 2, 3);
}, 1000);
  • parseInt() 数制转换,不要忽略第二个参数指定基数
1
EC3中以字母o开头的字符串被当做八进制处理,而在EC5中已经改变,为了避免意外,应 该总是指定基数参数,尽管默认是10
  • 团队里使用同一套缩进方案,tab或space缩进
1
比起纠结于具体的规范,团队里总是执行同一套方案更有价值!
  • 总是使用花括号{},尽管只有一行代码
1
2
花括号开始的位置,是同一行还是换行,这也是团队规范的问题了:统一就好,不必 纠结。
为了避免下一条谈到js引擎自动补全分好的问题,建议花括号开始于同一行,可终端JS分 号的自动补全。
  • 总是使用分号结束代码
1
因为JS引擎自动补全分号的机制,不小心的换行可能会中断代码逻辑,如return语句块 换到了下一行。
  • 命名规范,多种,选择一套喜欢的呗
    1. 构造函数首字母大写,如Person(){}
    2. 构造函数驼峰命名分割单词,如MyFunc(){}
    3. 变量名用下划线分割单词,如 my_name,这可以喝ECMAScript默认属性和方法的Camel标记法相区分
    4. 常量用全大写和下划线书写,如 MAX_WIDTH
    5. 全局变量名全部大写,如GLOBAL,并使用它来定义明明空间,如GLOBAL.name = “xiaolai”;GLOBAL.myMethod = function(){};
    6. 私有属性或方法用下划线前缀来表示,如 _index
    -

编码技巧

  • 访问全局对象
1
2
3
4
全局对象一般直接通过window属性来访问,但特殊情况下(如定义了名为window的局部 变量覆盖了全局的window)可使用匿名函数内的this来获得全局对象:
var global = (function(){
return this;
})();

新鲜概念

  • HTMLCollections对象
1
2
3
4
5
6
7
document.getElementsByName();
document.getElementsByClassName();
document.getElementsByTagName();
document.images; // 页面上所有的图片元素
document.links; // 所有a标签
document.forms; // 所有表单
document.forms[0].elements; // 页面上第一个表单中的所有域