标签:
JavaScript 是一种脚本语言,官方名称为 ECMAScript(因定义语言的标准为 ECMA-262)。JS 的主要特点:1. 语法类似于常见的高级语言,如 C 和 Java;2. 脚本语言,不需要编译就可以由解释器直接运行;3. 变量松散定义,属于弱类型语言;4. 面向对象的。 JS 最初是为网页设计而开发的,现在也是 Web 开发的重要语言。它支持对浏览器(浏览器对象模型,BOM)和 HTML 文档(文档对象模型,DOM)进行操作,而使网页呈现动态的交互特性。 严格的说,JS 只是 ECMAScript 的一种实现,是 ECMAScript 和 BOM、DOM 组成的一种 Web 开发技术。
基本数据类型:String,Boolean,Number,Undefined, Null
引用数据类型:Object(Array,Date,RegExp,Function)
那么问题来了,如何判断某变量是否为数组数据类型?
function isArray(value){
return Object.prototype.toString.call(value) == "[object Array]";
}
cookie是网站为了标示用户身份而储存在用户本地终端(Client Side)上的数据(通常经过加密)。cookie数据始终在同源的http请求中携带(即使不需要),记会在浏览器和服务器间来回传递。sessionStorage和localStorage不会自动把数据发给服务器,仅在本地保存。
var name = ‘罗恩’;
var aaa = {
name: ‘哈利’,
say: function () {
console.log(this.name);
}
}
var bbb = {
name: ‘赫敏’,
say: aaa.say
}
var ccc = aaa.say;
aaa.say(); //哈利
bbb.say(); //赫敏
ccc(); //罗恩
f1(f2);
f1.on(‘done‘,f2);
f1: jQuery.publish("done");
f2: jQuery.subscribe("done", f2);
f1().then(f2);
NaN
是 Not a Number 的缩写,JavaScript 的一种特殊数值,其类型是 Number,可以通过 isNaN(param)
来判断一个值是否是 NaN
:
console.log(isNaN(NaN)); //true
console.log(isNaN(23)); //false
console.log(isNaN(‘ds‘)); //true
console.log(isNaN(‘32131sdasd‘)); //true
console.log(NaN === NaN); //false
console.log(NaN === undefined); //false
console.log(typeof NaN); //number
console.log(Object.prototype.toString.call(NaN)); //[object Number]
ES6 中,isNaN()
成为了 Number 的静态方法:Number.isNaN()
console.log(0.1 + 0.2); //0.30000000000000004
console.log(0.1 + 0.2 == 0.3); //false
JavaScript 中的 number 类型就是浮点型,JavaScript 中的浮点数采用IEEE-754 格式的规定,这是一种二进制表示法,可以精确地表示分数,比如1/2,1/8,1/1024,每个浮点数占64位。但是,二进制浮点数表示法并不能精确的表示类似0.1这样 的简单的数字,会有舍入误差。
由于采用二进制,JavaScript 也不能有限表示 1/10、1/2 等这样的分数。在二进制中,1/10(0.1)被表示为0.00110011001100110011……
注意 0011
是无限重复的,这是舍入误差造成的,所以对于 0.1 + 0.2 这样的运算,操作数会先被转成二进制,然后再计算:
0.1 => 0.0001 1001 1001 1001…(无限循环)
0.2 => 0.0011 0011 0011 0011…(无限循环)
双精度浮点数的小数部分最多支持 52 位,所以两者相加之后得到这么一串 0.0100110011001100110011001100110011001100…因浮点数小数位的限制而截断的二进制数字,这时候,再把它转换为十进制,就成了 0.30000000000000004。
对于保证浮点数计算的正确性,有两种常见方式。
function add(num1, num2){
let r1, r2, m;
r1 = (‘‘+num1).split(‘.‘)[1].length;
r2 = (‘‘+num2).split(‘.‘)[1].length;
m = Math.pow(10,Math.max(r1,r2));
return (num1 * m + num2 * m) / m;
}
console.log(add(0.1,0.2)); //0.3
console.log(add(0.15,0.2256)); //0.3756
toPrecision()
和 toFixed()
方法,**注意,方法的返回值字符串。
function add(x, y) {
return x.toPrecision() + y.toPrecision()
}
console.log(add(0.1,0.2)); //"0.10.2"
isInteger(x)
来判断 x 是否是整数可以将 x
转换成10进制,判断和本身是不是相等即可:
function isInteger(x) {
return parseInt(x, 10) === x;
}
ES6 对数值进行了扩展,提供了静态方法 isInteger()
来判断参数是否是整数:
Number.isInteger(25) // true
Number.isInteger(25.0) // true
Number.isInteger(25.1) // false
Number.isInteger("15") // false
Number.isInteger(true) // false
JavaScript能够准确表示的整数范围在 -2^53
到 2^53
之间(不含两个端点),超过这个范围,无法精确表示这个值。ES6 引入了Number.MAX_SAFE_INTEGER
和 Number.MIN_SAFE_INTEGER
这两个常量,用来表示这个范围的上下限,并提供了 Number.isSafeInteger()
来判断整数是否是安全型整数。
AMD 是 RequireJS 在推广过程中对模块定义的规范化产出。CMD 是 SeaJS 在推广过程中对模块定义的规范化产出。主要区别是
AMD和CMD最大的区别是对依赖模块的执行时机处理不同,注意不是加载的时机或者方式不同
很多人说requireJS是异步加载模块,SeaJS是同步加载模块,这么理解实际上是不准确的,其实加载模块都是异步的,只不过AMD依赖前置,js可以方便知道依赖模块是谁,立即加载,而CMD就近依赖,需要使用把模块变为字符串解析一遍才知道依赖了那些模块,这也是很多人诟病CMD的一点,牺牲性能来带来开发的便利性,实际上解析模块用的时间短到可以忽略
为什么我们说两个的区别是依赖模块执行时机不同,为什么很多人认为AMD是异步的,CMD是同步的(除了名字的原因。。。)
同样都是异步加载模块,AMD在加载模块完成后就会执行改模块,所有模块都加载执行完后会进入require的回调函数,执行主逻辑,这样的效果就是依赖模块的执行顺序和书写顺序不一定一致,看网络速度,哪个先下载下来,哪个先执行,但是主逻辑一定在所有依赖加载完成后才执行
CMD加载完某个依赖模块后并不执行,只是下载而已,在所有依赖模块加载完成后进入主逻辑,遇到require语句的时候才执行对应的模块,这样模块的执行顺序和书写顺序是完全一致的
这也是很多人说AMD用户体验好,因为没有延迟,依赖模块提前执行了,CMD性能好,因为只有用户需要的时候才执行的原因
AMD
推荐的风格通过返回一个对象做为模块对象,CommonJS
的风格通过对module.exports
或exports
的属性赋值来达到暴露模块对象的目的。
顺便提一下:CommonJS是适用于服务器端的规范,NodeJS即是它的一种实现,CommonJS定义的模块分为:{模块引用(require)} {模块定义(exports)} {模块标识(module)}。 require()用来引入外部模块;exports对象用于导出当前模块的方法或变量,唯一的导出口;module对象就代表模块本身。
//CommonJS规范写法
//sum.js
exports.sum = function(){//做加操作};
//calculate.js
var math = require(‘sum‘);
exports.add = function(n){
return math.sum(val,n);
};
// CMD
define(function(require, exports, module) {
var a = require(‘./a‘)
a.doSomething()
// 此处略去 100 行
var b = require(‘./b‘) // 依赖可以就近书写
b.doSomething();
//本模块的导出接口
exports.each = function (arr) {
// 实现代码
};
exports.log = function (str) {
// 实现代码
};
})
// AMD就只有一个接口:define(id?,dependencies?,factory);
define([‘./a‘, ‘./b‘], function(a, b) { // 依赖必须一开始就写好
a.doSomething()
// 此处略去 100 行
b.doSomething();
//返回本模块的导出接口
var myModule = {
doStuff:function(){
console.log(‘Yay! Stuff‘);
}
}
return myModule;
...
})
//AMD还有一个require方法主要用来在顶层 JavaScript 文件中或须要动态读取依赖时加载代码
require([‘foo‘, ‘bar‘], function ( foo, bar ) {
// 这里写其余的代码
foo.doSomething();
//返回本模块的导出接口
var myModule = {
doStuff:function(){
console.log(‘Yay! Stuff‘);
}
}
return myModule;
});
1.通过jsonp跨域
script
标签自身的限制决定的。2.通过修改document.domain来跨子域(iframe)
3.隐藏的iframe+window.name跨域
4.iframe+跨文档消息传递(XDM)
5.跨域资源共享 CORS
6.Web Sockets
跨域请求并非是浏览器限制了发起跨站请求,而是请求可以正常发起,到达服务器端,但是服务器返回的结果会被浏览器拦截。
1.对象字面量
2.Object构造函数
mygirl=new Object();
ES5新方法构建对象,再添加属性
3.工厂模式
工厂模式虽然解决了创建多个相似对象的问题,但却没有解决对象识别的问题(即怎样知道一个对象的类型)。
4.构造函数模式
使用构造函数的主要问题,就是每个方法都要在每个实例上重新创建一遍。
5.原型模式
function Girl(){
}
在Girl.prototype上添加属性和方法
var mygirl=new Girl();
特点:原型中所有属性是被所有实例共享的,这种共享对于函数非常合适,但是对于基本属性就显得不是很合适,尤其是对于组合使用原型模式和构造函数创建对象包含引用类型值的属性来说,问题就比较突出了。
6.组合使用原型模式和构造函数创建对象(推荐)
创建自定义类型的最常见方式,就是组合使用构造函数模式与原型模式。构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。
7.动态原型模式
相对于组合模式,就是把原型上添加方法的步骤放在构造函数中,然后根据构造函数中是否已经存在该方法来决定添不添加
8.寄生构造函数模式
相对于工厂模式就是把函数当做构造函数调用
9.稳妥构造函数模式
1.借助原型链
function SuperType(){
this.colors = ["red", "blue", "green"];
}
function SubType(){
}
//继承了 SuperType
SubType.prototype = new SuperType();
使用原型链会出现两个问题,一是如果原型中包含引用类型值,子类所有实例会共享这个引用类型;二是没有办法在不影响所有对象实例的情况下,给超类型的构造函数传递参数。
2.借助构造函数
function SuperType(name){
this.name = name;
}
function SubType(){
//继承了 SuperType,同时还传递了参数
SuperType.call(this, "Nicholas");
//实例属性
this.age = 29;
}
能够解决第一种方法原型中包含引用类型值所带来问题,也能向超类型构造函数传递参数。问题是如果仅仅是借用构造函数,那么也将无法避免构造函数模式存在的问题——方法都在构造函数中定义,因此函数复用就无从谈起了。
3.组合继承(推荐)
组合继承( combination inheritance),有时候也叫做伪经典继承,指的是将原型链和借用构造函数的技术组合到一块,从而发挥二者之长的一种继承模式。其背后的思路是使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。这样,既通过在原型上定义方法实现了函数复用,又能够保证每个实例都有它自己的属性。下面来看一个例子。
function SuperType(name){
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function(){
alert(this.name);
};
function SubType(name, age){
//继承属性
SuperType.call(this, name);
this.age = age;
}
//继承方法
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function(){
alert(this.age);
};
var instance1 = new SubType("Nicholas", 29);
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
instance1.sayName(); //"Nicholas";
instance1.sayAge(); //29
var instance2 = new SubType("Greg", 27);
alert(instance2.colors); //"red,blue,green"
instance2.sayName(); //"Greg";
instance2.sayAge(); //27
4.原型式继承
function object(o){
function F(){}
F.prototype = o;
return new F();
}
在 object() 函数内部,先创建了一个临时性的构造函数,然后将传入的对象作为这个构造函数的原型,最后返回了这个临时类型的一个新实例。ECMAScript 5 通过新增 Object.create() 方法规范化了原型式继承。
var person = {
name: "Nicholas",
friends: ["Shelby", "Court", "Van"]
};
var anotherPerson = Object.create(person);
anotherPerson.name = "Greg";
anotherPerson.friends.push("Rob");
alert(person.friends); //"Shelby,Court,Van,Rob"
在没有必要兴师动众地创建构造函数,而只想让一个对象与另一个对象保持类似的情况下,原型式继承是完全可以胜任的。不过别忘了,包含引用类型值的属性始终都会共享相应的值,就像使用原型模式一样。
5.寄生式继承
function createAnother(original){
var clone = object(original); //通过调用函数创建一个新对象,也可以使用其他类似的方法
clone.sayHi = function(){ //以某种方式来增强这个对象
alert("hi");
};
return clone; //返回这个对象
}
6.寄生组合式继承
function inheritPrototype(subType, superType){
var prototype = object(superType.prototype); //创建对象
prototype.constructor = subType; //增强对象
subType.prototype = prototype; //指定对象
}
1、声明函数
最普通最标准的声明函数方法,包括函数名及函数体。
function fn1(){}
2、创建匿名函数表达式
创建一个变量,这个变量的内容为一个函数
var fn1=function (){}
注意采用这种方法创建的函数为匿名函数,即没有函数name
3、创建具名函数表达式
创建一个变量,内容为一个带有名称的函数
var fn1=function xxcanghai(){};
注意:具名函数表达式的函数名只能在创建函数内部使用
4、Function构造函数
可以给 Function 构造函数传一个函数字符串,返回包含这个字符串命令的函数,此种方法创建的是匿名函数。
5、自执行函数
(function(){alert(1);})();
(function fn1(){alert(1);})();
自执行函数属于上述的“函数表达式”,规则相同
参考:http://www.cnblogs.com/xxcanghai/p/4991870.html
call方法与apply方法的作用是一样的,都是为了改变函数内部的this指向。区别仅在于传入的参数形式的不同。
apply函数接受两个参数,第一个参数指定了函数体内this对象的指向,第二个参数为一个可以下标访问的集合,这个集合可以使数组,也可以是类数组,apply方法把这个集合中的元素作为参数传递给被调用的函数。
call方法传入的参数数量是不固定的,跟apply相同的是,第一个参数也是代表函数体内this对象的指向,从第二个参数开始往后,是一组参数序列,每个参数被依次传入函数。
Notes
现代的浏览器大多采用标记清除的方法来进行垃圾回收,其基本步骤如下:
function bindEvent()
{
var obj=document.createElement("XXX");
obj.onclick=function(){
//Even if it‘s a empty function
}
}
函数将间接引用所有它能访问的对象。obj.onclick这个函数中 可以访问外部的变量obj 所以他引用了obj,而obj又引用了它,因此这个事件绑定将会造成内存泄露.解决办法是可以把函数卸载外面。
function bindEvent()
{
var obj=document.createElement("XXX");
obj.onclick=onclickHandler;
}
function onclickHandler(){
//do something
}
另外对于事件应该在unload中解除循环引用的属性置为null
var parentDiv = document.createElement("div");
var childDiv = document.createElement("div");
document.body.appendChild(parentDiv);
parentDiv.appendChild(childDiv);
解决方法:
从内到外执行appendChild:
var parentDiv = document.createElement("div");
var childDiv = document.createElement("div");
parentDiv.appendChild(childDiv);
document.body.appendChild(parentDiv);
var someResource = getData();
setInterval(function() {
var node = document.getElementById(‘Node‘);
if(node) {
// 处理 node 和 someResource
node.innerHTML = JSON.stringify(someResource));
}
}, 1000);
此例说明了什么:与节点或数据关联的计时器不再需要,node 对象可以删除,整个回调函数也不需要了。可是,计时器回调函数仍然没被回收(计时器停止才会被回收)。同时,someResource 如果存储了大量的数据,也是无法被回收的。
参考:
Javascript内存泄漏
4类 JavaScript 内存泄漏及如何避免
如何检测浏览器内存泄漏
Chrome 提供了一套很棒的检测 JavaScript内存占用的工具。与内存相关的两个重要的工具:timeline
和 profiles
。具体参考Chrome开发者工具之JavaScript内存分析
Ajax 的全称是Asynchronous JavaScript and XML,其中,Asynchronous 是异步的意思,它有别于传统web开发中采用的同步的方式。
Ajax的原理简单来说通过XmlHttpRequest对象来向服务器发异步请求,从服务器获得数据,然后用javascript来操作DOM而更新页面。
XMLHttpRequest是ajax的核心机制,它是在IE5中首先引入的,是一种支持异步请求的技术。简单的说,也就是javascript可以及时向服务器提出请求和处理响应,而不阻塞用户。达到无刷新的效果。
XMLHttpRequest这个对象的属性有:
- 0 (未初始化) 对象已建立,但是尚未初始化(尚未调用open方法)
- 1 (初始化) 对象已建立,尚未调用send方法
- 2 (发送数据) send方法已调用,但是当前的状态及http头未知
- 3 (数据传送中) 已接收部分数据,因为响应及http头不全,这时通过responseBody和responseText获取部分数据会出现错误,
- 4 (完成) 数据接收完毕,此时可以通过通过responseXml和responseText获取完整的回应数据
function createXHR() {
if (XMLHttpRequest) {
return new XMLHttpRequest();
}else if (ActiveObject) {
var versions=["MSXML2.XMLHttp.6.0","MSXML2.XMLHttp.3.0","MSXML2.XMLHttp"];
for (var i = 0,len=versions.length; i < len; i++) {
try{
var xhr=new ActiveObject(versions[i]);
if (xhr) {
return xhr;
}
}catch(e){
return false;
}
}
}else{
throw new Error("No XHR object available.");
}
}
var xhr=createXHR();
xhr.onreadystatechange=function(){
if (xhr.readyState===4) {
if ((xhr.status>=200 && xhr.status<300)||xhr.status===304) {
console.log(xhr.responseText);
}else{
console.log("Request was unsuccessful:"+xhr.status);
}
}
};
/*Get请求
数据键值需要使用encodeURIComponent编码*/
xhr.open("get","example.txt?name1=value1&name2=value2",true);//true表示异步
xhr.setRequestHeader("Myheader","Myvaule");//在open方法之后,send方法之前设置请求头
xhr.send(null);
/*POST请求
数据键值需要使用encodeURIComponent编码*/
xhr.open("post","example.php",true);//true表示异步
xhr.setRequestHeader("Myheader","Myvaule");//在open方法之后,send方法之前设置请求头
/*send方法的参数为要发送的数据,格式为name1=value1&name2=value2;
如果想模拟表单,可以添加请求头Content-type:application/x-www-form-urlencoded
*/
xhr.send(data);
1.使用闭包代替小范围使用全局变量
2.函数外或在其他函数中访问某一函数内部的参数
3.在函数执行之前为要执行的函数提供具体参数
如:setTimeOut
setInterval
xhr.addEventListener("load",functionName, false);
如果functionName需要参数 怎么办呢
function functionNameFnc(a){ return function(){//做functionName该做的事情 已经可以用参数了 a } } xhr.addEventListener("load",functionNameFnc(a), false);
4.为节点循环绑定click事件,在事件函数中使用当次循环的值或节点,而不是最后一次循环的值或节点
4.封装私有变量
红宝书中提供:
1.使用闭包可以在JavaScript中模拟块级作用域
2.闭包可以用于在对象中创建私有变量;
函数绑定、函数柯里化
什么???不知道事件委托,呵呵!!自行百度或者查阅红宝书
优点
缺点
前端模块化开发的作用
<script src="util.js"></script>
<script src="dialog.js"></script>
使用requireJS加载模块的简要流程(网上找的,叙述的不是很好,大概那个意思)
1.我们在使用requireJS时,都会把所有的js交给requireJS来管理,也就是我们的页面上只引入一个require.js,把data-main指向我们的main.js。
2.通过我们在main.js里面定义的require方法或者define方法,requireJS会把这些依赖和回调方法都用一个数据结构保存起来。
3.当页面加载时,requireJS会根据这些依赖预先把需要的js通过document.createElement的方法引入到dom中,这样,被引入dom中的script便会运行。
4.由于我们依赖的js也是要按照requireJS的规范来写的,所以他们也会有define或者require方法,同样类似第二步这样循环向上查找依赖,同样会把他们村起来。
5.当我们的js里需要用到依赖所返回的结果时(通常是一个key value类型的object),requireJS便会把之前那个保存回调方法的数据结构里面的方法拿出来并且运行,然后把结果给需要依赖的方法。
模块加载基本原理
<script>
设置一个属性用来标识模块 id, 作用后面会提到。3.获取当前文件路径:document.currentScript
获取到正确的文件路径,才能正确判断依赖文件路径
a.js 里可能是 define( id, factory ) 或者是 define( factory ),后者被称为匿名模块。那么当 define(factory) 被执行的时候,我们怎么知道当前被定义的是哪个模块呢,具体地说,这个匿名模块的实际模块 id 是什么? 答案是通过 document.currentScript 获取当前执行的<script>
,然后通过上面给 script 设置的属性来得到模块 id。需要注意的是,低级浏览器是不支持 currentScript 的,这里需要进行浏览器兼容。还可以通过 script.onload 将script.src带过去来处理这个事情。
4.依赖分析
在继续讲之前,需要先简单介绍下模块的生命周期。模块在被 Define 之后并不是马上可以用了,在你执行它的 factory 方法来生产出最终的 export 之前,你需要保证它的依赖是可用的。那么首先就要先把依赖分析出来。简单来说,就是通过 toString 这个方法得到 factory 的内容,然后用正则去匹配其中的 require( ‘moduleId‘ )。当然也可以不用正则。
5.递归加载
在分析出模块的依赖之后,我们需要递归去加载依赖模块。用伪代码来表达大概是这样的:
Module.prototype.load = function () {
var deps = this.getDeps();
for (var i = 0; i < deps.length; i++) {
var m = deps[i];
if (m.state < STATUS.LOADED) {
m.load();
}
}
this.state = STATUS.LOADED;
}
参考:https://www.zhihu.com/question/21157540/answer/33583597
var foo = {name: ‘kitten‘}
foo.name; // kitten
foo[‘name‘]; // kitten
var get = ‘name‘;
foo[get]; // kitten
foo.1234; // SyntaxError
foo[‘1234‘]; // works
JavaScript
对象通过标记清除的方式进行垃圾回收,但BOM与DOM对象却是通过引用计数回收垃圾的, 也就是说只要涉及BOM
及DOM就会出现循环引用问题。使用闭包主要是为了设计私有的方法和变量。闭包的优点是可以避免全局变量的污染,缺点是闭包会常驻内存,会增大内存使用量,使用不当很容易造成内存泄露。在js中,函数即闭包,只有函数才会产生作用域的概念
闭包有三个特性:
1.函数嵌套函数
2.函数内部可以引用外部的参数和变量
3.参数和变量不会被垃圾回收机制回收
createDocumentFragment() //创建一个DOM片段
createElement() //创建一个具体的元素
createTextNode() //创建一个文本节点
操作的都是子节点,调用时要先取父节点(parentNode)
appendChild()
removeChild()
replaceChild()
insertBefore() //并没有insertAfter()
可以自己编写一个insertAfter函数
function insertAfter(newElement,targetElement){
var parent=targetElemnt.parentNode;
if(parent.lastChild==targetElement){
parent.appendChild(newElement)
}else{
parent.insertBefore(newElement,targetElement.nextSlibing)
}
}
其他方法
cloneNode()//一个参数,为true时,深赋值,复制节点及其整个子节点树,为false时只复制节点本身
normalize()//删除空文本节点或者合并相邻文本节点
getElementsByTagName() //通过标签名称
getElementsByName() //通过元素的Name属性的值(IE容错能力较强,
会得到一个数组,其中包括id等于name值的)
getElementById() //通过元素Id,唯一性
getElementByClassName()//html5
querySelector()
querySelectorAll()
标签:
原文地址:http://www.cnblogs.com/star91/p/5973509.html