lovecn / lovecn.github.io

个人记录
5 stars 5 forks source link

javascript语言精粹 #3

Open lovecn opened 10 years ago

lovecn commented 10 years ago

第二章:语法 1.标识符

标识符由一个字母开头,其后可加一个或多个字母、数字或下划线。不允许使用保留字。 undefined,NaNInfinity却不是保留字。JS不允许使用保留字来命名参数或者变量,也不允许在对象字面量中,或者在一个属性存取表达式的点号之后,使用保留字作为对象的属性名. ps:可以 undefined = 'test';不可var if = 'test';

2.数字

数字类型在内部表示为64位的浮点数。1和1.0是相同的。如果一个数字字面量有指数构成,则其值为由e之前的部分乘以10的e之后的部分的次方计算出来的值,所以100和10e2是相同的。NaN是一个不等于任何值,甚至包括其自身的值。用isNaN(number)检测NaN

ps:0.1+0.2!=0.3 转换下(0.1*10+0.2*10)/10==0.3就可以了。isNaN(NaN);//true但它并不靠谱因为isNaN('NaN');//true

3.字符串

字符串字面量可以被包围在双引号或单引号之中,包含0个或多个字符。js中的所有字符都为16位的字符集。\为转移字符,\u约定数字字符编码。如'A'==='\u0041' ps:==(相等运算法)vs ===(严格相等运算符) JS对象和其本身相等,和其他任何对象不相等。({})==({})//false ===首先计算其操作数的值,然后比较,比较过程无任何类型转换。 ==如果两个操作数不是同一类型的,则相等运算符进行一些类型转换进行比较。 ==这里截取JS相等表格上的的两张图片让大家更为直观的感受下。

字符串一旦创建无法改变,同python的不可变类型字符串和元组var age=22;age[0]=18;console.log(age);//22想回到18是不可能的,通过+可以连接字符串形成一个新的字符串。'c'+'a'+'t'==='cat'就很好理解了,也可以用concat方法'c'.concat('a').concat('t')

4.语句

当var语句被用在函数的内部时,他定义了这个函数的私有变量。 switch、while、for、和do语句允许一个可选的前置label配合break一起使用, 语句的执行顺序:从上到下。JS可用过条件语句(if,switch),循环语句(fo,while,do),强制跳转语句(return,break,throw)和函数调用来改变执行顺序。 false,null,undefined,空字符串”,数字0,数字NaN都为假,其余皆为真。 for循环有两种形式,for(var i=0; i < arr.length,i++){}和for(i in arr){},后者枚举一个对象的所有属性名(或键名)但不包括,object.hasOwnProperty(variable)来检测是否为该对象的成员,还是从原型链里找到的。 异常处理:throw{name:'test',message:'error'} JS不允许在return关键字和表示式之间换行,同样不允许break关键字和标签之间换行 +=可以用来加法运算或字符串连接

if([]){ alert(true);//output true } else{ alert(false); } return { name:'test' } for(myvar in obj){ if(object.hasOwnProperty(myvar)){

.....

  }
 }

5.表达式

最简单的表达式是字面量值(比如字符串或数字)、变量、内置的值(true、false、null、undefined、NaN和Infinity)、以new开头的调用表达式、以delete开头的属性存取表达式、在圆括号中的表达式、以一个前缀运算符作为开头的表达式,或者表达式后面跟着:

  • 一个运算符与另一个表达式;
  • 三元运算符?后面跟着另一个表达式,然后接一个:,再然后接第3个表达式;
  • 一个函数调用;
  • 一个属性提取表达式。 type of 运算符产生的值有‘number’ ‘string’ ‘boolean’ ‘undefined’ ‘function’和’object’如果运算数是一个数组或null,那么结果是‘object’
    typeof null;//object 检测null可通过variable===null 区分对象和nullif(variable&&typeof variable==='object'){//variable是一个对象或数组。}

6.字面量

对象字面量是一种方便指定新对象的表示法。属性名可是标识符或者字符串。var oabj={name:'js'}

第3章

1.对象字变量

JavaScript中简单类型包括数字、字符串、布尔值、null值和undefined,其他所有的值都为对象(数组是对象,函数是对象,正则表达式是对象,对象当然也是对象) var empty_object={}; var stooge={ "first-name":"xxx", "last-name:"xxx" } 在对象字面量中,如果属性名是一个合法的JavaScript标识符且不是保留字,并不强制要求用引号括住属性名。所以用引号括住“fisrt-name”是必须的,是否括住first_name才是可选的了

2.检索

要检索对象中包含的值,可以采用在[]后缀中括住一个字符串表达式的方式。若字符串是一个常数且他是一个合法的JavaScript标识符而非保留字,那么也可以用.表示法代替,优先使用.表示法,因为更紧凑可读性更好。 ` stooge["first-name"] //xxx flight.departure.city //xxx 若检索一个并不存在的成员元素的值,则返回undefined。 ||运算符可以用来填充默认值 var status=flight.status||"unkown";
尝试检索一个undefined值将会导致TypeError异常。可通过&&避免错误。 flight.equipment//undefined flight.equipment.model//throw "TypeError" flight.equipment&&flight.equipment.model//undefined

3.更新

对象中的值可以通过复制语句来更新,若属性名已经存在于对象中,那么该属性的值被替换,如果对象之前并未拥有这个属性名,则该属性会被扩充到该对象中。 stooge["first-name"]='bbbbb' stooge["second-name"]='ccccc'

4.引用

对象通过引用来传递。它们永远不会被拷贝。 var x=chouchou; x.nickname='huang'; var nick=chouchou.nickname; //因为x和chouchou是指向同一个对象的引用,所以nick也为'huang' var a={},b={},c={}; //a,b,c每个都引用不同的空对象。 a=b=c={}; //a,b,c都引用同一个空对象。

5.原型(prototype)

每一个对象都连接到一个原型对象,并且它可以从中继承属性,所有通过对象字面量创建的对象都连接到Object.prototype这个JavaScript的标准对象。 if(typeof Object.create !== 'function') { Object.create =function (o) { var F=function () {}; F.prototype=o; return new F(); } } var another_chouchou=Object.create(chouchou);

6.反射

检查对象并确定有什么属性很容易,只要试着去检索该属性并验证所取得的值。可用typeof typeof flight.toString //'function' typeof flight.constructor //'function'

7.枚举

for in 语句可用来遍历一个对象中所有的属性名。该枚举过程将会列出所有的属性,包括函数和你可能不关心原型链中的属性。所以我们需要过滤,常用的过滤器是hasOwnProperty以及typeof来排除函数。 属性名出现顺序不确定,要以确定的顺序应创建一个数组,在其中以正确的顺序包含属性名。 var i; var properties=[ 'fistr-name', 'middle-name', 'last-name' 'profession' ]; for(i = 0; i < properties.length;i +=1 ){ document.writeln(properties[i]+':'+ another[propertites[i]]); }

8.删除

delete运算符可以用来删除对象的属性。它不会触及原型链中的任何对象。删除对象的属性可能让来自原型链中的属性浮现出来。

9.减少全局变量污染

JavaScript可以很随意的定义全局变量。但全局变量减弱了程序的灵活性,应予以避免。最小化使用全局变量的方法是在你的应用中只创建一个全局变量。

var MYAPP={}; MYAPP.name={ "first-name":"xxx", "last-name":"xxxx" };

第4章函数

1.函数对象

函数就是对象,对象字面量产生的对象连接到Object.prototype,函数对象连接到Function.prototype,函数对象有prototype属性,它的值就是一个拥有constructor属性且为改函数的对象(尼玛,还能再绕点吗),看代码理解 function get(){} get.prototype.constructor===get

2.函数字面量

 var add =function(a,b){
 return a+b;
 }

3.调用

除了声明的参数,还有2个附加的参数this和arguments,javascript有4种调用模式:方法调用,函数调用,构造函数调用,apply/call调用,this的指向是由它所在函数调用的上下文决定的,而不是由它所在函数定义的上下文决定的。

//给类型加方法
Function.prototype.method = function (name, func) {
    this.prototype[name] = func;
    return this;
};

//实例化一个构造函数
if(typeof Object.beget !=='function'){
    Object.beget=function(o){
        var F=function () {};
        F.prototype=o;
        return new F();
    };
}

//异常处理
try{
    document.writeln(document.body);
}
catch(e){
document.writeln(e.name+':'+e.message);    
}

//闭包
var add_the_handles = function (nodes ) {
     var i;
    for(i = 0;i < nodes.length ; i+= 1) {
          nodes[i].onclick = function (e){
            alert(i);
          }
     }
};
add_the_handles 函数目的是给每个时间处理器一个唯一值(i).因为事件处理器函数绑定了变量i,而不是函数在构造时的变量i的值.
改正:
var add_the_handlers=function (nodes){
    var i;
    for(i=0;i<nodes.length;i+=1){
        nodes[i].onclick=function (i){
            return function (e){
                alert(i);// i 保存着这一块的值, 在内存里面
            };
        }(i);//注意这里的 "();" 是函数直接就执行了, 然后把 i 作为参数进去
    }
};
add_the_handlers(document.body.childNodes);
// 闭包让这些变量的值始终保持在内存中

//创建包含10个0的数组
Array.dim=function(dimension,initial){
    var a=[],i;
    for(i=0;i<dimension;i+=1){
        a[i]=initial;
    }
    return a;
}
var myArray=Array.dim(10,0);

//转义字符 如同php的htmlentities,替换方法php可以直接用strtr
if (typeof String.prototype.entityify !== 'function') {  
    String.prototype.entityify = function() {  
        var character = {  
            '<'      : '&lt;',  
            '>'      : '&gt;',  
            '&'     : '&amp;',  
            '"'     : '&quot;',  
            "'"     : '&#039;'  
        };  
              //每匹配一次函数调用一次,函数的返回值被用做替换文本
        return function () {  
            return this.replace(/[<>&"']/g,   
                function(c) { //第一个参数为整个匹配的文本,第二个参数为第一个分组捕获的文本 
                    return character[c];  //函数的匹配返回值,作为每次的匹配替换值
                }  
            );  
        };  
    }();  
}  

console.log('<>&'.entityify())//&lt;&gt;&amp;
var text = '0123456789'.spilt('',5);//[0,1,2,3,4]
全局变量 :函数之外定义var foo = value;window.foo=value; foo =value;
作用域:代码块中没有作用域({}中包含的代码),建议在函数开头声明所有变量
自动插入分号:return 
{name:value}//error
return {name:value}
parseInt('16')//16  parseInt('16gfdgf')//16遇到非数字停止解析与Number('16gfdgf');//naN不同
浮点数:0.1+0.2!=0.3 先转化为整数计算
判断数字 
var isNumber = function isNumber(value){
return typeof value === 'number' && isFinite(value);
}
setTimeout settimeInterval 当参数为字符串会同eval一样处理,建议用函数参数
switch穿越:不可缺少break
new 当实例化对象忘记new 会同调用普通函数一样,this为全局对象
对象的复制同php的引用赋值
var a={name:'test1'}
var b=a;
a.name='test2'
console.log(b.name);//test2
深度复制
var a={name:'test1'}
var b= JSON.parse(JSON.stringify(a));
a.name='test2'
console.log(b.name);//test1
var numbers = [4, 2, 5, 1, 3],
 n1 = numbers.slice(0),//对数组深复制,这里n2和numbers还是一个对象,但n1是另一个对象,只是对象的内容一样而已。
numbers[0]=0
n1//[4, 2, 5, 1, 3]

var numbers = [4, 2, 5, 1, 3],
    n1 = numbers.slice(0),
    n2 = numbers;
n1.sort(function(a, b) {
    return a - b;
});
交换值
//逗号运算符从左到右计算两个或多个操作数并返回最后一个操作数的值。
第一种

var a = 1, b = 2;
a = [b, b = a][0];
第二种

var a = 1, b = 2;
a = [b][b = a, 0];
[
    { name: "Robin Van PurseStrings", age: 30 },
    { name: "Theo Walcott", age: 24 },
    { name: "Bacary Sagna", age: 28  }
].sort(function(obj1, obj2) {
    // 实现增序排列:前者的 age 小于后者
    return obj1.age - obj2.age;
});
//对中文的排序
 var charArray = ["中","啊","家","高"];  
            charArray.sort(function(a,b){return a.localeCompare(b);});  
            for(var charIndex in charArray){  
                console.log(charArray[charIndex]);  
            }  
//利用arguments实现不定参数的相加
var sum = function (){
    var i,sum = 0;
    for(i=0;i<arguments.length;i++){
        sum+=arguments[i];
    }
    return sum;
}

sum(4,8,15,16,23,42)

处理类数组对象(arguments,document.getElementbyTagName)的最好方法是将其转化为数组。
Array.prototype.slice.call(arguments)
//关于基本类型和引用类型,有点绕
var a = {"x": 1};    //对象是引用类型,这里把对象{"x": 1}的引用地址赋值给变量a
var b = a;    //把a所引用的对象地址赋值给b,此时a和b指向同一个对象
a.x = 2;    //把a所指向对象的属性x设置为2,此时该对象为{"x": 2}
b.x;     //由于a和b指向同一个对象,所以b.x = a.x = 2

a = {"x":3};    //这里重新对a进行赋值,把一个新对象{"x": 3}的引用地址赋值给变量a,此时a指向这个新对象{"x": 3},而b仍然指向原来的对象{"x": 2}
console.log(b.x);    //这里输出2
{}!={}//true
关于{}  []转化为数字和字符的区别
[].valueOf();//[] [].toString();//'' ({}).valueOf();// Object {} ({}).toString();//"[object Object]"
{} + [] // => {}; +[] => 0

[] + {} // => '' + '[object Object]' => '[object Object]'

{} + [] == [] + {} // => {}; 0 == '[object Object]' => false

({} + [] == [] + {}) // => '[object Object]' + '' == '' + '[Object Object]' => true

1. {}放在语句开头的时候,被当做括代码的大括号;不在开头的时候,被当做括对象的大括号。
2. 单目运算符 `+ val` 就是把val强转为数字
3. +两边只要有非数字,就把两边转为字符串,做字符串连接。
4. 调用toString(),空数组转为空字符串,空对象转为'[object Object]'

判断一个变量是否是一个数组的方法是:
Object.prototype.toString.call(arr)==='[object Array]'
var obj = new Object();
function isEmptyObject(obj) {
for (var i in obj) {
return false;
}
return true;
}
isEmptyObject(obj);

自动执行
void运算符就是为了保证返回值是undefined,以此避免浏览器显示
void( function( ){
console.log('something');
} )( );
和
( function( ){
console.log('something');
} )( );
本来的匿名函数的返回值也是undefined. void也只把返回值置为undefined.
for in key的顺序不定,可将key放一个数组
var i ,pro=['first','second','three'];
for(i=0;i<pro.length;i++){
console.log(obj[pro[i]]);
}
函数总有个返回值,如果没有指定,返回undefined,如果函数调用前加new,且返回的不是一个对象,就返回this
Number.prototyep.int=function(){return Math[this<0?'ceil':'floor'](this)}

作用域控制变量和参数的可见性和生命周期
内部函数可以访问定义他的外部函数的参数和变量(除了this和arguments)