Saber 酱的抱枕

Fly me to the moon

04/29
2017
学习

JavaScript获取浏览器使用的语言

有时候,我们可能需要为不同国家的用户提供个性化服务,那么在浏览器里,我们可以使用JavaScript来获取UA里的语言设置,以此来判断用户的语言环境。

代码如下:

var language = navigator.language || navigator.browserLanguage;
if (language.indexOf('zh') > -1) {
	language = "chinese"; //中文
} else if (language.indexOf('en') > -1) {
	language = "english"; //英文
} else if (language.indexOf('ja') > -1) {
	language = "japanese"; //日文
} else if (language.indexOf('nl') > -1) {
	language = "dutch"; //荷兰语
} else if (language.indexOf('fr') > -1) {
	language = "french"; //法语
} else if (language.indexOf('de') > -1) {
	language = "german"; //德语
} else if (language.indexOf('it') > -1) {
	language = "italian"; //意大利
} else if (language.indexOf('pt') > -1) {
	language = "portuguese"; //葡萄牙
} else if (language.indexOf('es') > -1) {
	language = "Spanish"; //西班牙
} else if (language.indexOf('sv') > -1) {
	language = "swedish"; //瑞典
}

navigator.language是chrome和firefox所拥有的属性,navigator.browserLanguage则是IE独有。默认的话获取到的值是和操作系统的语言保持一致的,不过用户也可以在浏览器内自行更改。

JavaScript获取浏览器使用的语言

04/26
2017
学习

JavaScript中阻止事件冒泡

JavaScript中的event.stopPropagation方法用于阻止事件冒泡,这样该事件不会继续向上级元素传递。

不过今天我想讲的是event.stopImmediatePropagation,它除了具有event.stopPropagation方法的阻止事件冒泡的功能外,还有另一个功能,就是会中止执行在它后面添加/执行的同类型事件。也就是说干完我这件事件,后面的同类型的事情就都不用干了。

示例:

document.body.addEventListener("click", function () {
	console.log("1");
});
document.body.addEventListener("click", function (ev) {
	// 在它后面添加的同类型函数不会再执行了,但是早于它添加的函数还是会执行
	ev.stopImmediatePropagation();
	console.log("2");
});
document.body.addEventListener("click", function () {
	console.log("3");
});

本来,由addEventListener添加的同类型事件会按照添加的顺序依次执行。这个例子中就是点击页面会依次输出1、2、3。

在2中加了ev.stopImmediatePropagation()之后,第二个添加的事件执行完,后面添加的同类型事件就不会执行了——不会输出3了。

注意:

1.stopImmediatePropagation方法会阻止的是同类型的,比如在click事件里执行了stopImmediatePropagation方法,并不能阻止在其后添加的其他类型的事件。
2.在执行stopImmediatePropagation的事件里,在stopImmediatePropagation后面的代码依然会执行。(如上例会继续输出2)。

ps:jQuery有个event.isImmediatePropagationStopped方法来检测该事件上是否设置了stopImmediatePropagation方法。但是原生JavaScript里好像没自带这个检测的方法。

JavaScript中阻止事件冒泡

04/20
2017
学习

JavaScript中的call()和apply()

JavaScript中每个函数都有call()和apply()方法(呼叫和申请 XD),这两个方法都是为了改变函数的this值。简单地说,如果一个对象没有某个方法,但是其他人有,那么就可以用call()或apply()来借用这个方法。

call()和apply()方法的参数都分为两个部分,第一个参数是要作为this的对象,其他参数是要借用的函数的参数。

如果第一个参数为null或缺省,就会把this指向全局对象(window)。

如下代码:

var a={
	name:"saber",
	say:function () {
		console.log(this.name);
	}
},
	b={
		name:"我是2b"
	};
a.say.call(b);

a有个say()方法,可以说出自己的名字,但b没有这个方法。我们用a的say方法来call(呼叫)b,说我也不是谦虚,还是你来吧。这样a的this就变成了b,this.name也就是b的name了。

操作NodeList时常常借助数组的方法,代码如下:

var img=document.querySelectorAll("img");
[].slice.call(img,0,5);
[].forEach.call(img,function (no) {
	console.log(no.src);
});
//[]相当于Array.prototype的简写

Read More →

JavaScript中的call()和apply()

04/19
2017
学习

JavaScript的闭包

闭包是指有权访问另一个函数作用域中的变量的函数。

不用闭包的话,在函数外操作函数内的局部变量是不行的,因为局部变量的作用域只在函数内。我们可以通过闭包解决这个问题。

创建闭包的常见方式,就是在一个函数内部创建另一个函数,这样,被创建的函数就可以访问这部分作用域。

从形式上来说,给外部变量返回一个function,就形成了一个闭包。

function b() {
	var a=1;
	return function () {
		console.log(a);
	};
}
var c=b();

c(); // 1

上面代码是给外部变量c赋值了一个函数,可以输出函数b里面的变量a。

除了用return创建函数,我们也可以直接赋值。

如下代码,函数内有一个局部变量a,我们通过闭包,可以在函数外设置和读取a的值。

var set,get;

(function () {
	var a=0;
	set=function (s) {
		a=s;
		console.log(a);
	};
	get=function () {
		console.log(a);
	};
})();

set(2);
get();

使用闭包需要注意的问题有:
内存占用增加
this的指向。

JavaScript的闭包

04/11
2017
学习

JavaScript回收DOM节点的内存

如果我们要删除一个DOM节点,可能大家都会想到remove方法。如下:

var a=document.createElement("a");
// 设置内容等代码 ...
a.parentNode.removeChild(a);

Node.removeChild() 方法从DOM中删除一个子节点。返回删除的节点。

从返回值是个节点我们可以知道,removeChild之后,这个节点虽然在DOM里去掉了,但是它仍然存在在内存里。

虽说过一段时间不用,它就会被浏览器回收掉,但如果要重复的创建这个节点的话,那么每次removeChild时使用delete操作符,可以略微优化内存占用(大概……)。

如下:

var a=document.createElement("a");
// 设置内容等代码 ...
delete a.parentNode.removeChild(a);

今天我在chrome上做了多次实验,有一些发现:

1.使用delete之后,chrome浏览器并不会立即回收这部分内存,而是等差不多二十秒之后才回收。

2.使用delete之后,内存回收的比只remove要多一些。

3.即使使用delete,内存也不能完全回收,内存占用还是会一直增加。只是情况略有缓解。

4.有人说delete回收内存只能回收由JavaScript创建的节点,如果是原本html文档里的节点,delete是回收不了的。我试了试,对于原本html文档里的节点,用delete和只用remove没看出明显区别,大概这个说法是对的。

JavaScript回收DOM节点的内存

04/8
2017
学习

使用canvas将图片转换为base64编码

如果我们要在网页上处理图片,对于字符形式接收到的图片(比如用ajax获取的图片)可以将其转换为blob对象。但对于img标签(或通过new Image()对象获取的图片)来说,它们不是字符形式,而是html元素,不能转换成blob对象。

要使用这样的图片,我们可以先将图片绘制到canvas里,再使用canvas对象的toDataURL方法获取图片的base64编码(也就是data: URI)。

ps:blob对象和data: URI都可以用a标签的download属性来下载图片~

1.如下代码展示用image对象获取一张图片并转换为base64:

var dataURL="";
var Img = new Image();
Img.crossOrigin = "Anonymous";
Img.src="/f/head15.jpg";
Img.onload=function(){
	var canvas = document.createElement("canvas");
    canvas.width=Img.width;
    canvas.height=Img.height;
    canvas.getContext("2d").drawImage(Img,0,0,Img.width,Img.height); //将图片绘制到canvas中
    dataURL=canvas.toDataURL('image/png'); //转换图片为dataURL
};

注意image对象的crossOrigin 属性:

Img.crossOrigin = "Anonymous";

它在下面的情况中是有用的:
图片跨域了,但有个好消息是图片的服务器允许跨域。这时你加上这个crossOrigin属性就ok了,图片可以正常用。

但如果图片跨域了,并且图片的服务器【不允许】跨域,那你加上也没用,无解。

如果没解决跨域限制,canvas.toDataURL方法将报错,导致程序终止:

Uncaught DOMException: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.

2.如下代码展示从页面上的img标签获取图片内容,并将其转换为base64:

// 记得要在图片加载完成后执行
var image=document.querySelector("img"),
	canvas,
	dataURL;
image.onload=function () {
	canvas = document.createElement("canvas");
	canvas.width = image.width;
	canvas.height = image.height;
	canvas.getContext("2d").drawImage(image, 0, 0);
	dataURL=canvas.toDataURL('image/png'); //转换图片为dataURL
};

3.如果我们是要把图片用于下载的,那么对于色彩复杂、宽高较大的图片,用png格式显然体积会很大,可以转换成jpg的:

dataURL=canvas.toDataURL('image/jpeg',0.5);

经试验完全可用。另外chrome的话还可指定为 image/webp 。

参考资料来自MDN

4.扩展:现在各大浏览器只支持转换成png和jpeg格式,如果需要gif格式或bmp格式怎么办呢?可以用canvas2image.js

canvas2image支持将canvas里的图片数据转换为png、jpeg、gif、bmp格式,并可修改生成的图片的宽高。

你可以仅转换但不下载,也可直接下载。like this:

Canvas2Image.saveAsImage(canvasObj, width, height, type)
Canvas2Image.saveAsPNG(canvasObj, width, height)
Canvas2Image.saveAsJPEG(canvasObj, width, height)
Canvas2Image.saveAsGIF(canvasObj, width, height)
Canvas2Image.saveAsBMP(canvasObj, width, height)

Canvas2Image.convertToImage(canvasObj, width, height, type)
Canvas2Image.convertToPNG(canvasObj, width, height)
Canvas2Image.convertToJPEG(canvasObj, width, height)
Canvas2Image.convertToGIF(canvasObj, width, height)
Canvas2Image.convertToBMP(canvasObj, width, height)

使用canvas将图片转换为base64编码

04/1
2017
学习

JavaScript使代码延迟一段时间执行

JavaScript里说到延迟执行,一般想到的就是定时器。而且原生的方法里除了定时器,也没什么别的办法了。

但是定时器其实是一种插入机制,只针对它内部的代码做延迟,不能延迟整体代码的执行。

如果在某个时候,我们想让后面的代码全部延迟一定时间执行,只能把后面的代码全都放进定时器(或者改一下代码,只放调用部分)。多嵌套了一层,在代码的阅读和理解上产生了负面效果,很不舒服。

想要实现类似于其他程序的sleep函数的效果,我们可以用笨办法来做一个,实现整体代码的延迟。

示例:

function sleep(milliSeconds){
    var startTime = new Date().getTime();
    while (new Date().getTime() < startTime + milliSeconds);
}
console.log("1");
sleep(1000);
console.log("2");

不过使用sleep需要小心,因为它是整体延迟,会阻塞JavaScript进程,导致页面也卡住(类似于ajax的同步模式)。

比如上例代码,延迟一秒。如果在页面加载时执行,由于这一秒里JavaScript没执行完,所以页面加载时间就会增加一秒(+1s?),同时在这1秒里无法对页面进行操作。

那如果在页面加载完成后执行呢?这也会导致页面卡住1秒,因为sleep的代码导致CPU占用非常高,页面除了循环什么都不做了,导致用户在这1秒里无法对页面进行操作。

所以这个sleep虽然在效果上是真正的sleep(参考其他编程语言),但还是需要确认使用场景才能用,不能乱用。毕竟后台sleep用户只是多等了1秒,前台sleep却会导致页面卡住,对用户操作会产生直接的影响。

JavaScript使代码延迟一段时间执行

03/31
2017
学习

在JavaScript中创建Blob对象

JavaScript中的Blob对象可以存储二进制数据,并且还可以为数据指定MINE类型,因此适合在网页上储存文件用。

创建Blob对象需要使用new Blob()构造函数,它的语法如下:

Blob(blobParts[, options])

它有两个参数,第一个参数blobParts是要转化为Blob对象的数据,它必须在一个数组里。即使这份数据本来是独立的,也要创建一个数组把它包含到数组里。
第二个参数是可选的,使用Object类型的键值对来指定MINE类型。

示例:

var data='<b>aaa</b>';   
var blob=new Blob([data],{"type":"text/html"});   
console.log(blob);  

上例中是储存了一个html类型的Blob对象,实际使用中我们也可以储存其他文件类型,常见的有图片、json等。

相关资料:MDN上的相关内容
Read More →

在JavaScript中创建Blob对象

03/29
2017
学习 软件

仙尊的百度贴吧小尾巴

仙尊的百度贴吧小尾巴 UserScript

今天想找个百度贴吧小尾巴的脚本,但是有些功能太多,我用不着那么复杂的,所以自己写了个简单的。(简陋到没有前台设置界面)

安装此用户脚本

可以插入一条文字内容和(或)一张图片,分别在两个变量里设置(在脚本里修改):

仙尊的百度贴吧小尾巴 UserScript

如果两条都设置了内容,那么就会一起发出去。你也可以清空文字或图片对应的内容,之后就不会追加那条内容了。

要使用时,使用快捷键Shift+Enter提交,就会加上小尾巴。如果使用贴吧的正常方式——Ctrl+Enter或者点击提交按钮,就不会加上小尾巴。

仙尊的百度贴吧小尾巴