JavaScript深入浅出

200 阅读15分钟

@(前端)

1、包装对象:

原始类型:number、string、boolean、null、undefined,对象类型:Object:Function Array Date

var a = "string";
alert(a.length); //6 
a.t =3; 转换为包装对象。。之后临时对象会被销毁掉。
alert(a.t); //undefined;

2、类型检测

typeof:适合函数对象和基本类型的判断。返回一个字符串。返回值数是字符串。number,boolean,function,undefined,object。

对与其他对象类型 instanceof:判断对象类型,基于原型链的判断操作符。 obj[对象,如果是基本类型的话则会返回false] instanceof Object[函数对象,函数构造器,如果不是则会抛出typeerror异常] 原理:判断左操作数上的对象的原型链上是否有右边构造函数的prototype属性

任何一个构造函数都会有一个prototype对象属性

function Person(){}
function Student(){}
Student.prototype = new Person();//对象实例 ,对象实例会有原型指向Person.prototype
Student.prototype.constructor = Student
var bosn = new Student()
bosn instanceof Student //true
var one = new Person()
one instanceof Person //true
one instanceof Student //false
bosn instanceof Person //true

不同window活iframe间的对象类型检查不能使用instanceof

Object.prototype.toString

IE6/7/8 Object.prototype.toString.apply(null)返回“[object Object]”

constructor 任何一个对象都会有一个constructor属性,继承自原型。会指向一个构造器和构造函数, duck type 鸭子类型。判断length是不是数值。

题目

<!DOCTYPE HTML>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=gb18030">
    <title>Untitled Document</title>
    
</head>
<body>
    <script type="text/javascript">   
        /*
         * param1 Array 
         * param2 Array
         * return true or false
         */
        function arraysSimilar(arr1, arr2){
            if(!(arr1 instanceof Array)||!(arr2 instanceof Array)){
                return false;
            }
            if(arr1.length !==arr2.length){
                return false;
            }
            var i=0,n=arr1.length,countMap1= {},countMap2 = {}, t1,t1,TYPES = ['string','boolean','number','undefined','null','function','date','window'];
            for(;i<n;i++){
                t1 = typeof(arr1[i]);
                t2 = typeof(arr2[i]);
                if(countMap1[t1]){
                    countMap1[t1]++;
                }else{
                    countMap2[t1]=1;
                }
                if(countMap2[t2]){
                    countMap2[t2]++;
                }else{
                    countMap2[t2]=1;    
                }
                
            }
            function typeof(ele){
                var r;
                if(ele === null) r='null';
                else if(ele instanceof Array) r='array';
                else if(ele===window) r = 'window';
                else if (ele instanceof Date) r='date';
                else r = typeof ele;
                return r;
            }
            for(i=0;n=TYPES.length;i<n;i++){
                if(countMap1[TYPES[i]]!==countMap2[TYPES[i]]){
                    return false;
                }
            }
            return true;
        }
    </script>
    <script src="testData.js"></script>
</body>
</html>
3、 表达式

表达式是指能计算出值得任何可用程序单元。 表达式是一种JS短语,可以使JS解释器用来产生一个值。

原始表达式:常量、直接量 3.14,“test” ;关键字:null,this,true; 变量: i,k,j

原始表达式与原始表达式之间可以复合成复合表达式。

数组、对象的初始化表达式 [1,2] < == > new Array(1,2); [1,,,4] <==>[1,undefined,undefined,4] {x:1,y:2} <==> var o = new Object(); o.x = 1; o.y = 2;

函数表达式 var fe = function(){}; (function(){console.log("hello world";)})();

属性访问表达式 var o = {x:1}; o.x <==> o['x']

调用表达式 func();

对象创建表达式 new Func(1,2); new Object;

表达式 分为:原始表达式、初始化表达式、函数表达式、属性访问表达式、调用表达式、对象创建表达式

4、运算符

用于表达式之间用于运算。 分为:一元运算符、二元运算符、三元运算符

按功能分:赋值运算符、比较运算符、算术运算符、位运算符、逻辑运算符、字符串运算符、特殊运算符;

逗号运算符。 依次从左到右,结果值取最后的值。

delete运算符

ES9以后

in 运算符

运算符 instanceof,typeof 运算符new

运算符 this.

运算符 void

无论void后面是什么,都会返回undefined

运算符优先级

5、 JavaScript中的语句和严格模式

JavaScript程序由语句组成,语句遵守特定的语法规则。如:if语句,while语句,with语句等等

块 block 块语句常用于0~多个语句。快语句用一对花括号定义。

注意:没有块级作用域。

函数作用域:

声明语句 var var a = 1;
var a =b=1; //其中的b是隐式的创建了一个全局变量 避免出现这种情况,在定义多个变量时候采用逗号表达式; var a=1, b=1;

function foo(){
    var a=b=1;
}
foo();
console.log(typeof a);//'undefined'
console.log(typeof b); //'number'

try catch 语句 提供了异常捕获的机制,首先执行try块中的代码,由异常则被catch中捕获并处理 finally无论有没有错都会被执行

try{
    throw "test";
}catch(ex){
    console.log(ex); //test;
}finally{
    console.log('finally');
}

嵌套时候

例子1

例子2

function语句 函数声明:

函数表达式:

函数声明,的函数会先被预处理,而函数表达式的则不会。

for... in 语句

var p;
var obj = {x:1,y:2}
for(p in obj){} //遍历obj对象中的属性。

注意: 1、顺序不确定{具体的顺序依赖于引擎的实现} 2、enumerable为false时不会出现 3、for in 对象属性时受原型链影响{比如:obj对象原型链上的原型如果有其他的属性,enumerable为true。也会在for in 中出现}

switch语句:条件分支语句

循环语句

while(isTrue){
    //do sth.
}
do{
    //do sth.
}while(isTrue);
var i;
for(i=0;i<n;i++){
	 //do sth.
}

with语句 可以修改当前的作用域

在with可以直接用x输出1,而不用使用对象.x

在需要调用一层层很多关系的时候使用with会比较方便。如在浏览器里查看form下的所有元素的时候。

不建议使用with语句 原因:

  • 让JS引擎优化更难
  • 可读性差
  • 可被变量定义代替
  • 严格模式下被禁用
6、严格模式

严格模式是一种特殊的执行模式,它修复了JavaScript中部分语言上的不足,提供更强的错误检查,并增强安全性。

1、如何进入严格模式: 'use strict'; 指令指定部分或整体来执行,可以向上兼容。不支持严格模式则会忽略’use strict'

2、在严格模式中 不允许使用with语句

如果使用了with则会报错SyntaxError语法错误

3、在一般的模式下,可以不声明一个变量给它赋值,相当于声明了一个全局变量。

在严格模式下,给未声明的变量赋值则会报错。ReferenceError。

4、在严格模式下arguments变为参数的静态副本 在一般模式下,如果定义一个函数,调用它,和传参,于arguments会有绑定关系

在严格模式下arguments变为参数的静态副本

5、在严格模式下delete 参数、函数名会报错,SyntaxError语法错误。 在一般模式下,想把参数delete会返回false;删除不成功。

6、在严格模式下delete不可配置的属性会报错。

7、在严格模式下对象字面量重复属性名会报错。SystaxError语法错误。 一般模式下,重复属性名,以最后一个的值为准。

8、严格模式下,会禁制八进制字面量

9、eval、arguments变为关键字,不能作为变量、函数名

10、严格模式下,eval独立作业域

严格模式下

  • 不允许使用with
  • 所有变量必须声明,赋值给未声明的变量会报错,而不是隐式的创建全局变量
  • eval中的代码不能创建eval所在作用域下的变量、函数。而是为eval单独创建一个作用域,并在eval返回时丢弃。
  • 函数中的特殊对象arguments是静态副本,而不像非严格模式那样,修改arguments或修改参数变量会互相影响。
  • 删除configurable = false的属性时报错,而不是忽略。
  • 禁止八进制字面量,如010(八进制的8)会报错
  • eval,arguments变为关键字,不可作为变量名,函数名等。
  • 一般函数调用时(不是对象的方法调用,也不使用apply/call/bind等修改this)this指向null,而不是全局对象。
  • 若使用apply/call,当传入null或undefined时,this将指向null或undefined,而不是全局对象。。
  • 试图修改不可写属性(writable=false),在不可扩展的对象上添加属性时会报TypeError错,而不是忽略。
  • arguments.caller,arguments.callee被禁用。

严格模式是一种特殊的运行模式,它修了部分语言上的不足,提供更强的错误检查,并增强安全性。严格模式是向上兼容的。编写高质量,健壮性代码非常有用。

7、 JavaScript中的对象

对象中包含一系列的属性,这些属性是无序的。每个属性都有一个字符串key和对应的value.

探索对象的key

var obj={};
obj[1] =1;
obj['1']=2;
obj;//Object{1:2}
obj[{}] = true;
obj[{x:1}] = true;
obj;//Object{1:2,[object Object]:true}

对象结构

var obj = {};
obj.y=2;
obj.x=1;

function foo(){}
foo.prototype.z =3;
var obj = new foo();

1、对象创建-字面量

var obj1 = {x:1,y:2};
var obj2={
    x:1,
    y:2,
    o:{
	    z:3,
	    n:4
    }
}

2、创建对象-new(构造器的方法)/原型链

function foo(){} //每个对象都有proto属性。
foo.prototype.z = 3; //对象属性

var obj = new foo(); //用new取构造一个对象,特点:它的原型会执行构造器的prototype属性。
obj.y=2;
obj.x=1;

obj.x; //1
obj.y;//2
obj.z;//3 ,查找原型上的。
typeof obj.toString;//'function'
'z' in obj; //true;  继承来的。
obj.hasOwnProperty('z');//false 表示z不是obj直接对象上的,而是obj的原型链上的
obj.z=5;先看

对象创建-Object.create

var obj = Object.create({x:1});
obj.x //1
typeof obj.toString // "function"
obj.hasOwnProperty('x'); // false
var obj = Object.create(null);
obj.toString;//undefined

8、 属性操作

读写对象属性 属性异常 删除属性 检测属性 枚举属性

属性读写

var obj ={x:1,y:2};
obj.x;//1
obj["y"];//2

obj["x"]=3;
obj.y = 4;

  var obj = {x1:1,x2:2};
  var i = 1,n=2;
  for(;i<=n;i++){
	    console.log(obj['x'+i]);
  }
  //输出:1,2
  var p;
  for(p in obj){ //可能显示出原型链上的属性,且顺序是不确定的。
	  console.log(obj[p]);
  } 

属性读写-异常 尝试去读写一个不存在的属性

var obj = {x:1};
obj.y;//undefined
var yz = obj.y.z; //TypeError:Cannot read property 'z' of undefined
obj.y.z =2;//TypeError:Cannot set property 'z' of undefined

var yz;
if(obj.y){
	yz =obj.y.z
}
	
var yz = obj&&obj.y&&obj.y.z;

属性删除

var person = {age:28,title:'fe'};
delete person.age;//true
delete person['title'];/true
person.age;//undefined
delete person.age; //true

delete Object.prototype;//false 不允许被删除的

var descriptor = Object.getOwnPropertyDescriptor(Object,'prototype'); //获取一个属性中的所有标签,第一个参数:需要查看的对象,第二个:需要检测的属性 。descriptor描述器
descriptor.configurable;//false

属性删除

  • 用var 定义的全局变量或者局部变量不能被delete删除。函数声明也是同理都不可被delete掉
  • 隐式的创建一个全局的变量,可以被delete删除的变量。但是不推荐。
  • 在eval中定义的变量是可以被delete删除掉的

属性检测

var cat = new Object;
cat.legs = 4;
cat.name = "Kitty";

'legs' in cat;//true cat中有这个属性,
'abc' in cat; //false
'toString' in cat; //true,inherited property!! 原型链上的属性
	
cat.hasOwnProperty('legs');//true /cat对象上有这个属性
cat.hasOwnProperty('toString');//false//cat对象上没有这个属性,在原型链上才有这个toString。

cat.propertyIsEnumerable('legs');//true 查看这个对象是否是可枚举的
cat.propertyIsEnumerable('toString');//false

自定一个对象的属性,让它的枚举对象属性 是false.。使用Object的defineProperty的方法,第一个参数是添加属性的对象,第二个参数是:属性的名字,第三个是对象,要设置标签。 注意,自己用对象字面量,或者new Object(),创建的对象或通过赋值创建的属性,默认的是既可以枚举也可以写,也可以delete掉。 而用Object.defineProperty()默认所有标签都是false。 例如

Object.defineProperty(cat,'price',{enumerable:false,value:1000});
cat.propertyIsEnumerable('price');//false
cat.hasOwnProperty('price');//true

if(cat && cat.legs){
	cat.legs *=2;
}
if(cat.legs!==undefined){
	//only if cat.legs is not undefined
}
if(cat.legs!=undefined){
	//!==undefined,or !==null
}

属性枚举 枚举一些属性

var o = {x:1,y:2,z:3}; //变量。不可枚举的
'toString' in o; //true
o.propertyIsEnumerable('toString');//false
var key;
for(key in o){
    console.log(key);//x,y,z
}

//默认的是可枚举的
var obj = Object.create(o);
obj.a=4;
var key;
for(key in obj){
	console.log(key);//a,x,y,z
}     

var obj = Object.create(o);
obj.a = 4;
var key;
for(key in obj){
	//过滤掉原型链上的属性
	if(obj.hasOwnProperty(key)){
		console.log(key);//a
	}
}
9、getter setter方法

另一种读写属性的方法

var man = {
    name:'Bosn',
    weibo:'@Bosn',
    get age(){
	    return new Date().getFullYear()-1988;
    },
    set age(val){
	    console.log('Age can\'t be set to' + val);
    }
}
console.log(man.age);//27
man.age = 100; //Age can't be set to 100
console.log(man.age);//still 27

var man ={
    weibo:'@Bosn',
    $age:null,
    get age(){
	    if(this.$age == undefined){ //不管是null 还是undefined都能进来
		    return new Date().getFullYear() -1988;
	    }else{
		    return this.$age;
	    }
    },
    set age(val){
	    val =+val;
	    if(!isNaN(val)&&val>0&&val<150){
		    this.$age =+val;
	    }else{
		    throw new Error('Incorrect val =' +val);
	    }
    }
}

console.log(man.age);//27
man.age=100;
console.log(man.age);//100;
man.age = 'abc';//error:Incorrect val = NaN

10、 get/set与原型链

var o ={};
Object.defineProperty(o,'x',{value:1});//writable=false,configurable=false 只读属性,不可写
var obj = Object.create(o);
obj.x;//1
obj.x =200;
obj.x;//still 1, can't change it

Object.defineProperty(obj,'x',{writable:true,configurable:true,value:100});//可以写读操作
obj.x;//100
obj.x = 500;
obj.x;//500
11、属性标签

属性级的权限设置 例子: 返回一个对象,对象要上会示当前按属性下的所有标签。函数 接收两参数,第一个是需要判断的对象,第二个参数是字符串的属性名。 value:true,wriable:表示这个属性是否可以被修改,是否可写 enumerable:这个属性是否可以被遍历,是否能被枚举。会影响for in中是否出现 configurable:这个属性标签是否可以再被修改,另外也表示这个属性是否能被delete删除

Object.getOwnPropertyDescriptor({pro:true},'pro');
// Object {value:true,writable:true,enumerable:true,configurable:true}
Object.getOwnPropertyDescriptor({pro:true},'a');//undefined
var person = {};
Object.defineProperty(person,'name',{
	configurable:false;
	writable:false;
	enumerable:true;
	value:"Bosn Ma"
});

person.name;//Bosn Ma
person.name = 1;
person.name;//still Bosn Ma
delete person.name;//false
创建新的属性
Object.defineProperty(person,'type',{
	configurable:true, //
	writable:true, //
	enumerable:false, //
	value:"Object" //默认值是Object
}); //新的type属性。可以被修改,不能被修改
通过遍历发现type的属性存在
Object.keys(person);//["name"] //获取对象上的所有的key, 

属性的标签是可以重复的去设置。

12、对象标签

[[proto]],[[class]],[[extensible]]

原型标签__proto__

class标签 表示对象是那个类型 实际上没有直接的方式去查看class标签和修改它 可以间接的用Object.prototype.toString的方式来获取class

var toString = Object.prototype.toString;//把参数变为对象再去做处理。
function getType(o){return toString.call(o).slice(8,-1);} //截取第八个字符开始,到最后,去掉最后一个字符,的字符串;

toString.call(null);//"[object Null]"
getType(null);//"Null"
getType(undefined);//"Undefined"
getType(1);//"Number"
getType(new Number(1));//"Number"
typeof new Number(1);//"object"
getType(true);//"Boolean"
getType(new Boolean(true));//"Boolean"

extensible标签表示对象是否可扩展,对象上的属性是否可以被继续的添加

判断对象是否可扩展:Object.isExtensible(obj); 通过Object.preventExtensions(obj);不让其属性可以扩展 Object.seal(obj);将所有对象上的属性的configurable变成false。,不可修改对象上的属性,也不可删除对象上的属性。 通过Object.isSealed(obj);来判断这个对象是否被隐藏 Object.freeze(obj);//对象不能添加新属性,不能修改、删除属性,也不可用写属性的值。对象被冻结。 通过Object.isFrozen(obj)判断对象是否被冻结。

以上所有方法都是只是针对一个对象的。并不会影响对象的原型链。可以通过Object.prototype.all()来拿到对象的原型来遍历对象的原型链,来freeze。

序列化、其他对象方法

同JSON.stringify(obj);把对象转换成字符串。实现对象的序列化结果。 注意: 1、如果对象中的属性值是undefined则不会出现再序列化的结果当中。 2、如果属性值是NaN或者Infinity会转换为null 3、如果是时间的话会被转换为UTC的格式。

后端传回一个json的数据,转换为JavaScript的对象采用 JSON.parse(); 合法的json必须以花括号和双引号引起来{"x":1};

var obj = {
    x:1,
    y:2,
    o:{
	    o1:1,
	    o2:2,
	    toJSON:function(){
		    return this.o1+this.o2;
	    }
    }
};
JSON.stringify(obj);//"{"x":1,"y":2,"o":3}"

其他对象方法

var obj ={x:1,y:2};
obj.toString();//"[object Object]" 
obj.toString = fucntion(){
	return this.x+this.y
};
"Result"+obj;//"Result3",by toString
+obj;//3,from toString 加号会尝试把对象转换为数字

//尝试把对象装换成基本类型的时候会去调用的
obj.valueOf=function(){
	return this.x+this.y+100
};
+obj;//103,from valueOf
"Result"+obj;//still "Result 103"

toString和valueOf都存在的时候,不论是一元运算的+号还是二元运算的+的操作。都会尝试把对象转换为基本类型。会先去找valueOf的值作为结果,如果valueOf不存在,或者返回值不合法的时候则会找toString 并将其值作为结果返回。如果都不存在则会报错。

三、 JavaScript中的数组

数组是值的有序集合。每个值叫做元素,每个元素在数组中都有数字位置编号,也就是索引。JS中的数组是弱类型的,数组中可以含有不同类型的元素。数组元素甚至可以是对象或其他数组。

var arr =[1,true,null,undefined,{x:1},[1,2,3]];//数字字面量的方式存放

创建数组-字面量

可读性差,不建议使用下面的

数组的长度

创建数组-new Array

数组元素的读写 delete方式删除数组元素,值能被删除后,为undefined,但是数组的长度不变。

数组元素增删 动态的,无需指定大小 添加元素

arr.push()在数组尾部插入一个元素 arr[arr.length] = 4; 在数组尾部插入一个元素 arr.unshift(9);//在数组头部插入一个元素

删除元素

delete arr[2]; 下标为2的元素上的值为undefined

arr.length -=1; /可以删除掉最后一个尾部元素 arr.pop();//删除最尾部的元素 arr.shift();//删除头部的元素

数组迭代

JavaScript二位数组、稀疏数组

稀疏数组并不含有从0开始的连续索引。一般length属性值比实际元素个数大。

数组方法

{}=>Object.prototype {}=>Array.prototype

Array.prototype.join将数组转为字符串

Array.prototype.reverse将数组逆序

Array.prototype.sort 排序

Array.prototype.concat 数组合并

Array.prototype.slice 返回部分数组

Array.prototype.splice 数组拼接