前端学习

前端学习

@色少7年前

03/20
15:56
前端技能

Wepy-小程序踩坑记

引言

  • 用过原生开发的小程序也知道除了api 其他功能性的内容并不多对于需要做大型项目来说是比较难入手的,因此朋友推荐的wepy我就入坑鸟。。。
  • 这么一个跟vue的开发方式类似的框架,不过说起来跟vue类似,但是用起来还真不是那么简单。api开发还是和部分vue有出入,因此如下记录,入手的教程就不发了只发踩坑。

官方文档

下面是已经踩过的坑

 wepy 问题查找

标签中的指令简写

  • Vue类似
    • 对于动态赋值的属性可以使用 :attr=”value” 的方式
    • 对于绑定事件可以使用@click=”fn”的方式
  • data使用注意点
    • 对于视图中需要用到的数据,应该事先在data中定义一下,哪怕此时没有数据,也应该定义一个空值

WePY中的methods的使用

  • 只能声明页面的bindcatch事件,不能声明自定义方法
  • 自定义方法应该跟methods平级

this 运用

  • 小程序里修改data 里面的属性或者赋值都需要利用this.setdata wepy 基本就是利用this.属性即可。如果是异步返回或者更新dom需要this.$apply() 触发脏值检测

页面跳转

  • navigateTo()  redirectTo() 差别。
  •  navigateTo()保留当前页面,跳转到应用内的某个页面(即:显示顶部导航栏左上角返回按钮,可以有返回路径)
  • redirectTo()关闭当前页面,跳转到应用内的某个页面(即:不显示左上角返回按钮,如需要返回在页面内自己添加按钮写路径或者利用wx.navigateBack()和 getCurrentPages() 获取当前的页面栈,决定需要返回几层。
  • 简单来说如果你需要tabbar有返回就用navigateTo,不需要就用redirectTo
  • 只能用 switchTab() 跳转到 tabbar 页面

文件上传

  • 上传文件没有传统html中的文件域(<input type=”file”/>),要想上传文件只能使用API uploadFile()

更新DOM$apply

  • 如果需要更新DOM,应该在随后调用组件实例的$apply方法,才能更新到页面中
this.name=”abc”;
this.$apply()
  • PS:对于性能要求较高的应用,不要频繁的调用$apply()更新DOM,可以根据实际情况更新父组件向子组件传递数据,通过props的方式传递
  • 如果需要传递动态数据,加上.sync的修饰符就可以解决(:prop.snyc=’item’
  • 如果需要子组件数据同步到父组件,需要在定义props的时候设置twoWay:true
  • (所有异步数据传递必须用$apply,同步的话才能使用.sync)

mixin

  • wepymixin,与vue中的mixin执行顺序相反
    • wepy中,会先执行组件自身的,再执行mixin中的
    • vue中对于钩子函数,会先执行mixin中的,再执行组件自身的;vuemethods如果和mixin同名,那么只会执行自身的方法

关于canvasbase64

  • 小程序中可以进行canvas相关操作,但是跟纯html中的canvas有所不同(api差异),canvas的使用都应该参照:小程序中的canvas

arrayBufferbase64互转

  • 本段内容在文档中是搜索不到的,但是确实是支持的,使用如下2种方式:
    • wx.arrayBufferToBase64(arrayBuffer)
    • wx.base64ToArrayBuffer(base64)

命名规范

  • 小程序内部定义的实例API都以$开头,所以我们在定义实例属性、方法的时候不能以$开头,以便区分

同名组件共享同一实例及数据

循环渲染组件时,容易出现组件数据相互污染。可以用最外层的组件监听事件冒泡以修改数据,同时触发事件的组件用setTimeout包裹,保证执行顺序。

循环渲染组件:

wepy的循环渲染组件,必须使用 <repeat>标签,或者微信官方的<block>标签(这两个标签都是不会渲染到dom的)否则就不会渲染成功。

组件component 没有 onLoad 等页面事件

页面中设置好 this.$broadcast(‘someEvent’, option);
组件监听事件则可以解决

page

页面类,继承自wepy.component,拥有页面所有的属性与方法。
全部属性继承自wepy.component。而wepy.component没有onLoad 等方法

Wepy-小程序踩坑记

@色少8年前

07/21
13:08
前端技能

React-native 异常

最近在捣鼓下rn,刚入门就一堆错误并且再xcode里面展示看到一头雾水。网上资料不是很多,对于前端来说简直就是入门程度难上加难。因此自我做个记录。
目前开发环境:
xcode:8.3.3
node:8.1.2
react-native-cli: 2.0.1
react-native: 0.45.1

新版本的rn 0.45系列一启动xcode模拟容易出错关键在于加载的包的下载问题-(要不就降级版本要不就是一步步查找缺失的文件包等)。

ios
一启动就报错

unpacking /.rncache/glog-0.3.4.tar.gz 关键字

产生原因:

/Users/mocoyu/.rncache 中 boost_1_63_0.tar.gz, double-conversion-1.1.5.tar.gz, folly-2016.09.26.00.tar.gz, glog-0.3.4.tar.gz 基本文件下载不完整
node_modules/react-native/third-party 文件不完整

以上基本都是第三方的模块没下载下来。

对于这种要不是没正确上网姿势(正确的翻wa11)要不就是下载不完全
因此解决办法:
1、删除 .rncache 后重新下载,或手动下载后放入 .rncache 中
2、把以上文件解压后放入 node_modules/react-native/third-party 下
3、Clean & Build

.rncache 文件位置在:

显示系统隐藏文件办法,终端输入:
defaults write com.apple.finder AppleShowAllFiles -bool true //显示隐藏文件
defaults write com.apple.finder AppleShowAllFiles -bool false //不显示隐藏文件
命令运行之后需要重新加载Finder:快捷键option+command+esc,选中Finder,重新启动即可

 

如果以上你都解决不了那就是wa11问题,你需要如下操作
1、https://codeload.github.com/google/glog/tar.gz/v0.3.4
下载完毕后 放入/Users/YOUCOUNT/.rncache
2、开启了全局模式的墙,delete node_modules再从新执行一边安装
3、building前再切换墙自动代理模式(不然会有坑,比如make sure you’re running a packager server or have included a .jsbundle file in your application bundle的错误)

缺失哪些文件就在google的codeload里面找对应版本下载。

查看详细的Xcode编译错误

左侧点击跟项目目录 -> 选择右侧 Build Settings -> 选择 All & Combined -> 搜索框输入 Always Search User Paths -> 将 Always Search User Paths 设置为 Yes -> Clean -> Build

make sure you’re running a packager server or have included a .jsbundle file in your application bundle

基本遇到此类错误就是没有正确的翻 wa11,去把此处Mac中用于科学上网的shadowocks中的网络代理,从 全局代理 改为 自动代理模式 即可

React-native 异常

@色少8年前

05/11
19:17
前端技能

函数节流(throttle)与函数去抖(debounce)

项目优化种发现很多时候需要输入之后验证,因此用得频率比较高的2个函数debounce和throttle。还有以下场景往往由于事件频繁被触发,因而频繁执行DOM操作、资源加载等重行为,导致UI停顿甚至浏览器崩溃。

1. window对象的resize、scroll事件

2. 拖拽时的mousemove事件

3. 射击游戏中的mousedown、keydown事件

4. 文字输入、自动完成的keyup事件

实际上对于window的resize事件,实际需求大多为停止改变大小n毫秒后执行后续处理;而其他事件大多的需求是以一定的频率执行后续处理。针对这两种需求就出现了debounce和throttle两种解决办法。

debounce

debounce的关注点是空闲的间隔时间

 // 函数去抖(连续事件触发结束后只触发一次)
 // sample 1: _.debounce(function(){}, 1000)
 // 连续事件结束后的 1000ms 后触发
 // sample 1: _.debounce(function(){}, 1000, true)
 // 连续事件触发后立即触发(此时会忽略第二个参数)

_.debounce = function(func, wait, immediate) {
    // immediate默认为false
    var timeout, args, context, timestamp, result;

    var later = function() {
      // 当wait指定的时间间隔期间多次调用_.debounce返回的函数,则会不断更新timestamp的值,导致last < wait && last >= 0一直为true,从而不断启动新的计时器延时执行func
      var last = _.now() - timestamp;

      if (last < wait && last >= 0) {
        timeout = setTimeout(later, wait - last);
      } else {
        timeout = null;
        if (!immediate) {
          result = func.apply(context, args);
          if (!timeout) context = args = null;
        }
      }
    };

    return function() {
      context = this;
      args = arguments;
      timestamp = _.now();
      // 第一次调用该方法时,且immediate为true,则调用func函数
      var callNow = immediate && !timeout;
      // 在wait指定的时间间隔内首次调用该方法,则启动计时器定时调用func函数
      if (!timeout) timeout = setTimeout(later, wait);
      if (callNow) {
        result = func.apply(context, args);
        context = args = null;
      }

      return result;
    };
  };

Throttle

throttle 的关注点是连续的执行间隔时间

// 函数节流(如果有连续事件响应,则每间隔一定时间段触发) 
// 每间隔 wait(Number) milliseconds 触发一次 func 方法
// 如果 options 参数传入 {leading: false} 
// 那么不会马上触发(等待 wait milliseconds 后第一次触发 func) // 如果 options 参数传入 {trailing: false}
// 那么最后一次回调不会被触发
// **Notice: options 不能同时设置 leading 和 trailing 为 false** 
// 示例: 
// var throttled = _.throttle(updatePosition, 100);
// $(window).scroll(throttled); 
// 调用方式(注意看 A 和 B console.log 打印的位置):
// _.throttle(function, wait, [options]) 
// sample 1: _.throttle(function(){}, 1000)
// print: A, B, B, B ... 
// sample 2: _.throttle(function(){}, 1000, {leading: false}) 
// print: B, B, B, B ... 
// sample 3: _.throttle(function(){}, 1000, {trailing: false}) 
// print: A, A, A, A ...
 // ----------------------------------------- //


_.throttle = function(func, wait, options) { 

/* options的默认值 * 表示首次调用返回值方法时,会马上调用func;否则仅会记录当前时刻,当第二次调用的时间间隔超过wait时,才调用func。
 * options.leading = true; 
 * 表示当调用方法时,未到达wait指定的时间间隔,则启动计时器延迟调用func函数,若后续在既未达到wait指定的时间间隔和func函数又未被调用的情况下调用返回值方法,则被调用请求将被丢弃。
 * options.trailing = true; 
 * 注意:当options.trailing = false时,效果与上面的简单实现效果相同 
*/

   var context, args, result;
    var timeout = null;
    var previous = 0;
    if (!options) options = {};
    var later = function() {
      previous = options.leading === false ? 0 : _.now();
      timeout = null;
      result = func.apply(context, args);
      if (!timeout) context = args = null;
    };
    return function() {
      var now = _.now();
      if (!previous && options.leading === false) previous = now;
      // 计算剩余时间
      var remaining = wait - (now - previous);
      context = this;
      args = arguments;
      // 当到达wait指定的时间间隔,则调用func函数
      // 精彩之处:按理来说remaining <= 0已经足够证明已经到达wait的时间间隔,但这里还考虑到假如客户端修改了系统时间则马上执行func函数。
      if (remaining <= 0 || remaining > wait) {
        // 由于setTimeout存在最小时间精度问题,因此会存在到达wait的时间间隔,但之前设置的setTimeout操作还没被执行,因此为保险起见,这里先清理setTimeout操作
        if (timeout) {
          clearTimeout(timeout);
          timeout = null;
        }
        previous = now;
        result = func.apply(context, args);
        if (!timeout) context = args = null;
      } else if (!timeout && options.trailing !== false) {
        // options.trailing=true时,延时执行func函数
        timeout = setTimeout(later, remaining);
      }
      return result;
    };
  };

throttle 应用场景

  • DOM 元素的拖拽功能实现(mousemove)
  • 射击游戏的 mousedown/keydown 事件(单位时间只能发射一颗子弹)
  • 计算鼠标移动的距离(mousemove)
  • Canvas 模拟画板功能(mousemove)
  • 搜索联想(keyup)
  • 监听滚动事件判断是否到页面底部自动加载更多:给 scroll 加了 debounce 后,只有用户停止滚动后,才会判断是否到了页面底部;如果是 throttle 的话,只要页面滚动就会间隔一段时间判断一次

debounce 应用场景
函数去抖有哪些应用场景?哪些时候对于连续的事件响应我们只需要执行一次回调?

  • 每次 resize/scroll 触发统计事件
  • 文本输入的验证(连续输入文字后发送 AJAX 请求进行验证,验证一次就好)

函数节流(throttle)与函数去抖(debounce)

@色少8年前

05/11
13:35
前端技能

深入剖析 JavaScript 的深复制

http://www.cnblogs.com/charling/p/3452677.html
jQuery.extend()源码解读深拷贝或者浅拷贝
http://jerryzou.com/posts/dive-into-deep-clone-in-javascript/
深入剖析JavaScript 的深复制

记录如下:
1、jQuery.clone()——dom对象的复制
在 jQuery 中也有这么一个叫

1
$.clone()

的方法,可是它并不是用于一般的 JS 对象的深复制,而是用于 DOM 对象。
2、jQuery.extend()——深复制(递归extend)
3、lodash —— _.clone() / _.cloneDeep()

1
_.clone(obj, true)

等价于

1
_.cloneDeep(obj)

比较各个深复制方法

特性 jQuery lodash JSON.parse 所谓“拥抱未来的深复制实现”
浏览器兼容性 IE6+ (1.x) & IE9+ (2.x) IE6+ (Compatibility) & IE9+ (Modern) IE8+ IE9+
能够深复制存在环的对象 抛出异常 RangeError: Maximum call stack size exceeded 支持 抛出异常 TypeError: Converting circular structure to JSON 支持
对 Date, RegExp 的深复制支持 × 支持 × 支持
对 ES6 新引入的标准对象的深复制支持 × 支持 × ×
复制数组的属性 × 仅支持RegExp#exec返回的数组结果 × 支持
是否保留非源生对象的类型 × × × 支持
复制不可枚举元素 × × × ×
复制函数 × × × ×

深入剖析 JavaScript 的深复制

@色少10年前

06/23
13:35
前端技能

跨域小妙招总结

一直都说想找个大总结的文章关于跨域的处理奈何度娘上的资料太老旧,自己对于新的知识点又不是十分十分有研究。因此一直让懒癌发作下去。奈何最近做了双失青年,因此有了空余的时间去研究研究知识点和梳理一下,顺便自己动手风衣足食。废话不多说请看下文。提交上github的分享。希望可以多多讨论。

https://github.com/yusemo/javascript-cros/

 

 

跨域小妙招总结

@色少10年前

05/20
13:28
前端技能

数组去重的一些总结

废话不多说直接上题目!关于数组去重的例子

数组arr1和arr2,长度不固定,如何从arr1中删除和arr2中重复值的元素?

如:


var arr1=[1,3,4,6,7];

var arr2=[3,4,7];

====>

var arr3=[1,6]

console.log()

//*  ==================================================================================*//

思路1:

假如数组中的元素全是数字可以直接利用正则匹配

console.log(arr1.concat(arr2).sort().join(”).replace(/(w)1+/g,”).split(”))

思路2:

数组中包含其他类型的字符,比如:

var arr=[1,2,3,’1′,’2′,’3′,1,2]

利用hash表。把已经出现过的通过下标的形式存入一个object内。下标的引用要比用indexOf搜索数组快很多。

但是内存占用方面应该第二种方法比较多,因为多了一个hash表。这就是所谓的空间换时间。


Array.prototype.unique2 = function(){
    var n = {},r=[]; //n为hash表,r为临时数组
    for(var i = 0; i < this.length; i++) //遍历当前数组
      {
         if (!n[this[i]]) {
           //如果hash表中没有当前项
             n[this[i]] = true; //存入hash表
             r.push(this[i]); //把当前数组的当前项push到临时数组里面
          }
      }
      return r;
}

console.log([1,2,3,'1','2','3',1,2].unique2())

思路3:

先把数组排序,然后比较相邻的两个值。 排序的时候用的JS原生的sort方法,JS引擎内部应该是用的快速排序。


Array.prototype.unique3 = function(){
    this.sort();
    var re=[this[0]];
    for(var i = 1; i < this.length; i++){
       if( this[i] !== re[re.length-1]){
           re.push(this[i]);
       }
    }
    return re;
}

思路4:

利用数组的indexOf方法。此方法的目的是寻找存入参数在数组中第一次出现的位置。

很显然,js引擎在实现这个方法的时候会遍历数组直到找到目标为止。所以此函数会浪费掉很多时间。


Array.prototype.unique4 = function(){
    var n = [this[0]]; //结果数组
    for(var i = 1; i < this.length; i++) //从第二项开始遍历
    { //如果当前数组的第i项在当前数组中第一次出现的位置不是i,
      //那么表示第i项是重复的,忽略掉。否则存入结果数组
      if (this.indexOf(this[i]) == i) n.push(this[i]);
    }

    return n;
}

//*  =============================== 更新新的es5方法 ===========================*//

1、借助栈的方法

let unique = arr => {
  let res = [];
  while (arr.length > 0) {
    let item = arr.shift();
    res.indexOf(item) === -1 && res.push(item);
  }
  return res;
};
console.log(unique([1, 4, 5, 6, 6, 7, 7, 999, 999, 8, 7, 999]));

2、Array from + Set 方法
不考虑兼容性,这种去重的方法代码最少。但是无法去掉“{}”空对象


function unique (arr) { 
	return Array.from(new Set(arr)) 
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr))
//[1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {}, {}]

3、Set 方法
这种去重的方法代码最少。但是set的一个最大的特点就是数据不重复


function unique(arr) {
  return [...new Set(arr)];
}

console.log(unique([1, 2, 2, 3, 4, 4]));

4、indexOf() + lastIndexOf()方法

let unique = arr => {
  let result = [];
  arr.forEach((item, index) => {
    if (arr.indexOf(item, index) === arr.lastIndexOf(item) && result.indexOf(item) === -1) {
      result.push(item);
    }
  });
  return result;
};

let arr = [1, 4, 5, 6, 6, 7, 7, 999, 999, 8, 7, 999];
console.log(unique(arr));

5、includes()方法

function unique(arr) {
  let res = [];
  arr.forEach(item => {
    if (!res.includes(item)) {
      res.push(item);
    }
  });
  return res;
}

console.log(unique([1, 2, 3, 4, 3, 3, 3, 7, 6, 6, 9, 9]));

6、hasOwnProperty

function unique(arr) {
	 var obj = {}; 
	 return arr.filter(function(item, index, arr){
	 	 return obj.hasOwnProperty(typeof item + item) ? false : (obj[typeof item + item] = true) 
	 }) 
} 
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}]; 
console.log(unique(arr)) 
//[1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {…}] 
//所有 的都去重了

7、利用reduce+includes

function unique(arr){
	 return arr.reduce((prev,cur) => prev.includes(cur) ? prev : [...prev,cur], []);
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}]; 
console.log(unique(arr)); 
// [1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {…}, {…}]

8、利用Map数据结构去重

function arrayNonRepeatfy(arr) {
	 let map = new Map(); 
	 let array = new Array(); // 数组用于返回结果 
	 for (let i = 0; i < arr.length; i++) {
	 	 if(map .has(arr[i])) {// 如果有该key值 
	 	 	 map .set(arr[i], true); 
	 	 } 
	 	 else {
	 	 	 map .set(arr[i], false); // 如果没有该key值 
	 	 	 array .push(arr[i]);
	 	  } 
	 }
	 return array ; 
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}]; 
console.log(unique(arr)) 
//[1, "a", "true", true, 15, false, 1, {…}, null, NaN, NaN, "NaN", 0, "a", {…}, undefined]

9、利用filter


function unique(arr) { 
	return arr.filter(function(item, index, arr) {
	 	//当前元素,在原始数组中的第一个索引 === 当前索引值,否则返回当前元素 
	 	return arr.indexOf(item, 0) === index; 
	 }); 
}
 var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}]; console.log(unique(arr)) 
 //[1, "true", true, 15, false, undefined, null, "NaN", 0, "a", {…}, {…}]

10、简化map+filter 去重


function unique(arr) { 
	const res = new Map(); //定义常量 res,值为⼀个Map对象实例
    //返回arr数组过滤后的结果,结果为⼀个数组 过滤条件是,如果res中没有某个键如no,如没键值就直接arr,就设置这个键的值为1    
     return arr.filter((arr) => !res.has(arr.no) && res.set(arr.no, 1)) 
}
 var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}]; console.log(unique(arr)) 
 //[1, "true", true, 15, false, undefined, null, "NaN", 0, "a", {…}, {…}]

数组去重的一些总结

@色少10年前

03/13
23:40
前端技能

;!function(){}的一些理解

;!function(){…}

目的是防止在minify合并行的时候由于前一行缺少分号导致错误 比如:

alert(1)
!function(){…}

合并后变成 alert(1)!function… 就syntax error了 加一个分号就好了;

=================================================================

延展一下:
!function(){alert('aaa')}()        // true
+function(){alert('aaa')}()        // NaN
-function(){alert('aaa')}()        // NaN
~function(){alert('aaa')}()        // -1

因为这样的代码混淆了函数声明和函数调用,以这种方式声明的函数

1
a

,就应该以

1
a();

的方式调用。

但是括号则不同,它将一个函数声明转化成了一个表达式,解析器不再以函数声明的方式处理函数a,而是作为一个函数表达式处理,也因此只有在程序执行到函数a时它才能被访问。所以,任何消除函数声明和函数表达式间歧义的方法,都可以被解析器正确识别。所以,赋值,逻辑,甚至是逗号,各种操作符都可以告诉解析器,这个不是函数声明,它是个函数表达式。并且,对函数一元运算可以算的上是消除歧义最快的方式,感叹号只是其中之一,如果不在乎返回值,这些一元运算都是有效的; 

测试下()是性能最优。因此常用于 (function(){})();也叫:自执行函数表达式;

参考:

http://swordair.com/function-and-exclamation-mark/

http://suqing.iteye.com/blog/1981591

《Javascript高级程序设计(第3版)》 7.3 模仿块级作用域

;!function(){}的一些理解

@色少10年前

11/17
16:32
前端技能

【转】从一行代码里面学点JavaScript

翻译来自张小俊:http://www.html-js.com/article/A-day-to-learn-from-a-line-of-code-inside-the-JavaScript-study-JavaScript

enter image description here

从一行代码里面学点JavaScript

现如今,JavaScript无处不在,因此关于JavaScript的新知识也是层出不穷。JavaScript的特点在于,要学习它的语法入门简简单,但是要精通使用它的方式却是一件不容易的事。

来看看下面的这段代码,它来自于谷歌“名猿”Addy Osmani在几天前贴出的一段代码,它的作用是用来调试你的CSS层。全部代码只有三行,但是你绝对可以把它放在一行里面完成:

   [].forEach.call($$("*"),function(a){
  a.style.outline="1px solid #"+(~~(Math.random()*(1<<24))).toString(16)
})

现在,在你的Chrome浏览器的控制台中输入这段代码,你会发现不同HTML层都被使用不同的颜色添加了一个高亮的边框。是不是非常酷?但是,简单来说,这段代码只是首先获取了所有的页面元素,然后使用一个不同的颜色为它们添加了一个1ps的边框。想法很简单,但是真要实现起来却不是那么容易的一件事。在下面的内容中,我们将一起一步一步学习如何理解上面的这段代码。

选择页面中所有的元素

我们需要做的第一件事情是获取页面中所有的元素,在上面的代码中,Addy使用了一个Chrome浏览器中特有的函数$$。你可以在你的Chrome浏览器控制台中输入$$('a'),然后你就能得到一个当前页面中所有锚元素的列表。

$$函数是许多现代浏览器命令行API中的一个部分,它等价于document.querySelectorAll,你可以将一个CSS选择器作为这个函数的参数,然后你就能够获得当前页面中所有匹配这个CSS选择器的元素列表。如果你在浏览器控制台以外的地方,你可以使用document.querySelectorAll('*')来代替$$('*')。更多关于$$函数的详细内容可以查看Chrome开发者工具的文档。

当然,除了使用$$函数之外,我们还有一种更简单的方法,document.all,虽然这并不是一种很规范的使用方法,但是它几乎在每一个浏览器中都能运行成功。

迭代所有的元素

经过第一步,我们已经获得了页面内所有的元素,现在我们想做的事情是遍历每一个元素,然后为它们添加一个彩色边边框。但是上面的代码究竟是怎么一回事呢?

 
[].forEach.call( $$('*'), function( element ) { /* 在这里修改颜色 */ });

首先,我们通过选择器获得的列表是一个NodeLists对象,它和JavaScript中的数组有点像,你可以使用方括号来获取其中的节点,你也可以检查它其中包含多少个元素,但是它并没有实现数组包含的所有方法,因此我们并不能使用$$('*').forEach()来进行迭代。在JavaScript中,有好几个类似于数组但是并不是数组的对象,除了前面的NodeLists,还有函数的参数集合arguments,在这里我们可以使用call或apply函数将函数的方法运用到这些对象上。例如下面的例子:

   function say(name) {
     console.log( this + ' ' + name );
   }

   say.call( 'hola', 'Mike' ); // 打印 'hola Mike'

// 你也可以将这种方法有用在arguments对象上 function example( arg1, arg2, arg3 ) { return Array.prototype.slice.call(arguments, 1); // Returns [arg2, arg3] }

在Addy的代码中,使用了[].forEach.call而不是Array.prototype.forEach.call,二者等价,但是前者可以节省几个字节。

为元素添加颜色

为了让元素都有一个漂亮的边框,我们在上面的代码中使用了CSS属性outline。outline属性位于CSS盒模型之外,因此它并不影响元素的属性或者元素在布局中的位置,这对于我们来说非常有用。这个属性和修改border属性非常类似,因此下面的代码应该不会很难理解:

a.style.outline="1px solid #" + color

真正有趣的地方在于定义颜色部分:

~~(Math.random()*(1<<24))).toString(16)

天呐,上面的代码是什么意思?在JavaScript中,比特操作符并不是经常被使用,因此这里可能会让很多程序员感到很疑惑。

我们想达到的目的是活的一个十六进制格式的颜色例如白色对应的是FFFFFF,蓝色对应的是0000FF,或者随便一个颜色37f9ac。虽然我们人类喜欢十进制,但是我们的代码常常会需要十六进制的东西。

我们首先要学会如何使用toString函数将一个十进制的数组转换为一个十六进制整数。这个函数可以接受一个参数,如果参数缺省,默认为十进制,但是你完全可以使用别的数组:

 (30).toString(); // "30"
(30).toString(10); // "30"
(30).toString(16); // "1e" 十六进制
(30).toString(2); // "11110" 二进制
(30).toString(36); // "u" 36是允许的最大参数值

除此之外,你可以使用parseInt函数将十六进制数字转换为十进制。

 parseInt("30"); // "30"
parseInt("30", 10); // "30"
parseInt("1e", 16); // "30"
parseInt("11110", 2); // "30"
parseInt("u", 36); // "30"

因此,我们现在只需要一个位于0和ffffff之间的十六进制数,由于:

parseInt("ffffff", 16) == 16777215

而这里的16777215实际上是2^24-1。

如果你对二进制数学熟悉的话,你可能会知道1<<24 == 16777216。 再进一步,你每在1后面添加一个0,你就相当于多做了一次2的乘方:

1 // 1 == 2^0
100 // 4 == 2^2
10000 // 16 == 2^4
1000000000000000000000000 // 16777216 == 2^24

因此,在这里我们可以知道Math.random()*(1<<24)表示一个位于0和16777216之间的数。 但是这里并没有结束,因为Math.random返回的是一个浮点数,但是我们只想要整数部分。我们的代码中使用波浪号操作符来完成这件事。波浪操作符在JavaScript中被用来对一个变量进行取反。 但是我们在这里并不关心取反,我们指向获取整数部分。因此我们还可以知道两次取反可以去掉一个浮点数的小数部分,因此~~的作用相当于parseInt:

var a = 12.34, // ~~a = 12
    b = -1231.8754, // ~~b = -1231
    c = 3213.000001 // ~~c = 3213
;

~~a == parseInt(a, 10); // true
~~b == parseInt(b, 10); // true
~~c == parseInt(c, 10); // true


当然,我们还有一种更加简洁的方法,使用OR操作符:

~~a == 0|a == parseInt(a, 10)
~~b == 0|b == parseInt(b, 10)
~~c == 0|c == parseInt(c, 10)

最终,我们获得了一个位于0和16777216之间的随机整数,也就是我们想要的随机颜色。此时我们只需要使用toString(16)将它转化为十六进制数即可。

总结

现在,你已经完全理解了前面的这一行代码中的各个部分。作为一个程序员,我们应该在完成工作之后多问自己几遍为什么,还有没有更好更简洁的方法。当然,最应该做的事情当然是多阅读程序代码,也许你就能从某一行代码中学到很多新东西。

本文参考自Learning much javascript from one line of code,原文地址http://arqex.com/939/learning-much-javascript-one-line-code

确实非常融会贯通。感激国人大大的翻译。

【转】从一行代码里面学点JavaScript

@色少10年前

10/14
17:11
前端技能

【转】如何绕过chrome的弹窗拦截机制

转载来自:http://cssha.com/open-window-in-chrome/

在chrome的安全机制里面,非用户触发的window.open方法,是会被拦截的。举个例子:

var btn = $('#btn'); btn.click(function () {              //不会被拦截              window.open('http://cssha.com')      });

上面的代码中,window.open是用户触发的时候,是不会被拦截的,可以正常打开新窗口。再看下面这个:

var btn = $('#btn');
btn.click(function () {  
       $.ajax({          
          url: 'ooxx',     
          success: function (url) {                 
          //会被拦截                 
          window.open(url);             
          }         
      })     
});

那么,当用户点击按钮,发出ajax请求,如何在ajax请求完成后再打开新窗口,又不被拦截呢? 接着往下看:

var btn = $('#btn');
btn.click(function () {         
//打开一个不被拦截的新窗口         
var newWindow = window.open();
         $.ajax({ 
           url: 'ooxx', 
           success: function (url) {  
           //修改新窗口的url             
           newWindow.location.href = url;       
             }   
               }) 
 });

上面的代码中,用户安点击按钮的时候,先打开一个空白页,再发ajax请求,在ajax回调里面修改新窗口的.location.href,这样就不会被拦截啦啦啦啦~~~

 

【转】如何绕过chrome的弹窗拦截机制

@色少11年前

09/1
15:38
前端技能

【转】关于Gzip的部署

[转]http://www.cnblogs.com/linzhenjie/archive/2013/03/05/2943635.html
apache启用gzip压缩方法
一、gzip介绍

Gzip是一种流行的文件压缩算法,现在的应用十分广泛,尤其是在Linux平台。当应用Gzip压缩到一个纯文本文件时,效果是非常明显的,大约可以减少70%以上的文件大小。这取决于文件中的内容。

利用Apache中的Gzip模块,我们可以使用Gzip压缩算法来对Apache服务器发布的网页内容进行压缩后再传输到客户端浏览器。这样经过压缩后实际上降低了网络传输的字节数,最明显的好处就是可以加快网页加载的速度。

网页加载速度加快的好处不言而喻,除了节省流量,改善用户的浏览体验外,另一个潜在的好处是Gzip与搜索引擎的抓取工具有着更好的关系。例如 Google就可以通过直接读取gzip文件来比普通手工抓取更快地检索网页。在Google网站管理员工具(Google Webmaster Tools)中你可以看到,sitemap.xml.gz 是直接作为Sitemap被提交的。

而这些好处并不仅仅限于静态内容,PHP动态页面和其他动态生成的内容均可以通过使用Apache压缩模块压缩,加上其他的性能调整机制和相应的服务器端 缓存规则,这可以大大提高网站的性能。因此,对于部署在Linux服务器上的PHP程序,在服务器支持的情况下,我们建议你开启使用Gzip Web压缩。

二、Web服务器处理HTTP压缩的过程如下:

1. Web服务器接收到浏览器的HTTP请求后,检查浏览器是否支持HTTP压缩(Accept-Encoding 信息);

2. 如果浏览器支持HTTP压缩,Web服务器检查请求文件的后缀名;

3. 如果请求文件是HTML、CSS等静态文件,Web服务器到压缩缓冲目录中检查是否已经存在请求文件的最新压缩文件;

4. 如果请求文件的压缩文件不存在,Web服务器向浏览器返回未压缩的请求文件,并在压缩缓冲目录中存放请求文件的压缩文件;

5. 如果请求文件的最新压缩文件已经存在,则直接返回请求文件的压缩文件;

6. 如果请求文件是动态文件,Web服务器动态压缩内容并返回浏览器,压缩内容不存放到压缩缓存目录中。

下面是两个演示图:

开启使用Gzip后:

三、实施

Apache上利用Gzip压缩算法进行压缩的模块有两种:mod_gzip 和mod_deflate。 要使用Gzip Web压缩,请首先确定你的服务器开启了对这两个组件之一的支持。在Linux服务器上,现在已经有越来越多的空间商开放了对它们的支持,有的甚至是同时 支持这两个模块的。例如目前Godaddy、Bluehost及DreamHosts等空间商的服务器都已同时支持mod_gzip 和mod_deflate。

虽然使用Gzip同时也需要客户端浏览器的支持,不过不用担心,目前大部分浏览器都已经支持Gzip了,如IE、Mozilla Firefox、Opera、Chrome等。

通过查看HTTP头,我们可以快速判断使用的客户端浏览器是否支持接受gzip压缩。若发送的HTTP头中出现以下信息,则表明你的浏览器支持接受相应的gzip压缩:

Accept-Encoding: gzip 支持mod_gzip
Accept-Encoding: deflate 支持mod_deflate

Accept-Encoding: gzip,deflate 同时支持mod_gzip 和mod_deflate
  在apache2.0以上(包括apache2.0)的版中gzip压缩使用的是mod_deflate模块,下面是具体配置步骤 如下:

   1、修改Apache的http.conf文件,去除mod_deflate.so前面的注释

LoadModule deflate_module modules/mod_deflate.so

 2、在根目录中新建.htaccess文件,定制压缩规则

#GZIP压缩模块配置

#启用对特定MIME类型内容的压缩
SetOutputFilter DEFLATE
SetEnvIfNoCase Request_URI .(?:gif|jpe?g|png|exe|t?gz|zip|bz2|sit|rar|pdf|mov|avi|mp3|mp4|rm)$ no-gzip dont-vary #设置不对压缩的文件
AddOutputFilterByType DEFLATE text/html text/css text/plain text/xml application/x-httpd-php application/x-javascript #设置对压缩的文件

3、对指定的文件配置缓存的生存时间,去除mod_headers.so模块前面的注释

LoadModule headers_module modules/mod_headers.so

4、在根目录中新建.htaccess文件,定制压缩规则

#文件缓存时间配置

Header set Cache-Control "max-age=2592000"

里面的文件MIME类型可以根据自己情况添加,至于PDF 、图片、音乐文档之类的这些本身都已经高度压缩格式,重复压缩的作用不大,反而可能会因为增加CPU的处理时间及浏览器的渲染问题而降低性能。所以就没必要再通过Gzip压缩。通过以上设置后再查看返回的HTTP头,出现以下信息则表明返回的数据已经过压缩。即网站程序所配置的Gzip压缩已生效。

Content-Encoding: gzip
注:不管使用mod_gzip 还是mod_deflate,此处返回的信息都一样。因为它们都是实现的gzip压缩方式。

除此之外,还可以通过一些在线检查工具http://tool.chinaz.com/Gzips/来检测你的网站内容是否已经过Gzip压缩。

四、mod_gzip 和mod_deflate的主要区别是什么?使用哪个更好呢?

首先一个区别是安装它们的Apache Web服务器版本的差异。Apache 1.x系列没有内建网页压缩技术,所以才去用额外的第三方mod_gzip 模块来执行压缩。而Apache 2.x官方在开发的时候,就把网页压缩考虑进去,内建了mod_deflate 这个模块,用以取代mod_gzip。虽然两者都是使用的Gzip压缩算法,它们的运作原理是类似的。

第二个区别是压缩质量。mod_deflate 压缩速度略快而mod_gzip 的压缩比略高。一般默认情况下,mod_gzip 会比mod_deflate 多出4%~6%的压缩量。

那么,为什么使用mod_deflate?第三个区别是对服务器资源的占用。 一般来说mod_gzip 对服务器CPU的占用要高一些。mod_deflate 是专门为确保服务器的性能而使用的一个压缩模块,mod_deflate 需要较少的资源来压缩文件。这意味着在高流量的服务器,使用mod_deflate 可能会比mod_gzip 加载速度更快。

不太明白?简而言之,如果你的网站,每天不到1000独立访客,想要加快网页的加载速度,就使用mod_gzip。虽然会额外耗费一些服务器资源, 但也是值得的。如果你的网站每天超过1000独立访客,并且使用的是共享的虚拟主机,所分配系统资源有限的话,使用mod_deflate 将会是更好的选择。

另外,从Apache 2.0.45开始,mod_deflate 可使用DeflateCompressionLevel 指令来设置压缩级别。该指令的值可为1(压缩速度最快,最低的压缩质量)至9(最慢的压缩速度,压缩率最高)之间的整数,其默认值为6(压缩速度和压缩质 量较为平衡的值)。这个简单的变化更是使得mod_deflate 可以轻松媲美mod_gzip 的压缩。

P.S. 对于没有启用以上两种Gzip模块的虚拟空间,还可以退而求其次使用php的zlib函数库(同样需要查看服务器是否支持)来压缩文件,只是这种方法使用起来比较麻烦,而且一般会比较耗费服务器资源,请根据情况慎重使用。详细 php启用zlib压缩文件

【转】关于Gzip的部署