js类型, 值, 变量

类型:

基本类型:number,string, boolean,null, undefined,其中number,string,boolean都有wrapper objects,所以他们有对应的方法可以调用,但是null和undefined就没有。

其余的都是object类型。

普通的object类型是一个无序的key-value集合.

array,function,date,regular,error等是特殊的对象,js对这些特殊对象提供了额外的支持。

 

数字:

数学运算在js中是不报错的,比如除0,负数开方等,而是会返回特殊的结果。

js中任何数都是用二进制浮点数(binary floating-point numbers))表示的,所有无法分解为1/2(n)形式的小数都无法精确表示,比如0.1

console.log(0.3-0.2);//无法精确表示

 

String:

js使用utf-16编码来表示字符串

 

null undefined:

null是关键字,而undefined只是一个全局变量,在ECMA5之前undefined值是可以被改变的

 

Wrapper Objects:

三种基本类型:number,string和boolean都有对应的wrapper object,访问基本类型的属性或方法时,会自动进行装箱操作。比如string a="a"; a.substring(1);,调用substring方法时,会自动使用包装类将a装换成一个临时的String对象,即进行了如下的操作:var a = new String(a); 所以才可以调用a.substring方法,使用完毕后销毁。类似于java中的自动装箱。

 

var s="a";  
s.len=4;  
console.log(s.len);     //undefined  

 

 

 

理解上述代码,因为第二行代码执行时,由于访问了s的属性,此时s不是第一行中的s二是一个临时的String对象,自然在第三行中赋值时会出错。

也可以自己手动装箱:var s = new String(s);

注意:不能用length做试验,因为它和方法一样是字符串对象自带的,你去调用它也是临时生成一个String对象的,这会干扰你的判断。

var s="a";  
/*这里再用此句: s=new String(s),设置length也不会改变s.length的值。照此你可能会得出结论说:new String(s)并不能创建新对象,其实不是的,而是你打印的是s对象的自带属性length,而不是自定义属性len */ s.length =4;  
console.log(s.length);     //1  

 

同样,装箱有对应的拆箱操作,在必要的时候会把String对象拆箱为s。

 

immutable & mutable:

五种基本类型都是不可变的:number,boolean,string,undefined,null, 任何对他们的改变本质都是创建了一个新的变量。

基本类型的比较操作都是根据其值的,只有string不同,是根据其长度和每个index的字符。

object类型默认是比较引用的,比如Array。

 

类型转换:

对象转换成基本类型有如下两个方法:toString(), valueOf(), 一个是转成string,一个是转成数字。

 

function scope:

js的作用域是function scope,也就是函数内声明的局部变量在整个函数内部都是可以访问的,区别于很多语言的block scope.

用var声明的局部对象是不可以delete的。

hoisting 特性:函数内的所有变量都相当于自动在函数头部声明(注意,只是声明被移到头部,赋值部分位置不变)。所以有些js程序员喜欢在函数开头声明所有变量是有道理的。

 

[javascript]  view plain  copy
 
  1. function(){  
  2.      return a;  
  3.      var a=1;  
  4. }  
  5. //以上代码等价于以下代码  
  6. function(){  
  7.      var a;  
  8.      return a;  
  9.      a=1;  
  10. }  
  11.   
  12. //看看另一个代码  
  13. a = 1;  
  14. function f(){  
  15.      console.log(a);  
  16.      var a = 2;  
  17. }  
  18. f();     //输出undefined,而不是输出1,等价转换一下就明白了  
  19.   
  20. //上述代码等价一下代码  
  21. a = 1;  
  22. function f(){  
  23.      var a;  
  24.      console.log(a);  
  25.      a=2;  
  26. }  
  27. f();     //所以就很明显该输出undefined。  



 

 

eval, delete

直接调用eval(string),作用域就是当前代码的作用域,在很多实现中(除ie外),如果使用另一个名字调用eval,则作用域会自动变为全局作用域。

ie中有一个execScript和eval类似,但是作用域会变成全局作用域。

delete操作无法删除var定义的变量,delete只适合于删除某一对象的属性,但是delete操作是不会报错的。

delete不会影响父类的属性

在ECMA5 strict模式下,delete任何不能被delete的变量都会报错,并且无法删除configurable为false的属性

 

function

调用任何一个function时都有两种参数:arguments 和 context(隐式传递,即函数中的this);

四种调用方式:

1,函数调用, a(1);

2,方法调用,o.a(1);

3, 构造函数调用,new a(1);

4, 简介调用,a.call(this, 1); a.apply(this, 1);

函数调用和方法调用的区别是context的不同,函数调用的context一般是global(在E5 strict模式下是undefined),方法调用的context是o.

构造函数var a = new A(1); context是a, 如果A返回一个object类型,则a就是返回值,如果A返回的不是object类型,则返回值被忽略。

js不会检查函数的参数类型和参数个数,js中也不存在函数重载。

function hoisting

function声明和局部变量声明一样,也会被hoisting到当前作用域的顶部,赋值部分位置不变,所以:

var a=function(){};     //在此之前无法使用a(),因为只有var a;被升到顶部,而赋值部分位置不变

function a(){};     //在此之前就可以使用a,因为整个语句被升到顶部了。

method chaining

如果method总是返回this,那么可以进行链式操作,如jQuery。

 

closure

嵌套的函数声明即为闭包。

 

闭包的原理:函数的作用域是函数定义时的作用域,而不是函数调用时的作用。

闭包的实现:因为每产生一个函数都会生成一个新的scope chain, 一般情况函数结束后就没有对这个scope chaine的引用,因此scope chain被销毁,但是如果有闭包的存在,因为内部函数的scope chain引用了外部函数的scopechain,所以外部函数的scope chain不会被销毁。

 

如果调用多次外部函数,则会创造多个scope chain,因此每一个闭包访问的外部scope chain都是独立的:

 

[javascript]  view plain  copy
 
  1. function f(){  
  2. var a = 1;  
  3. return  function(){return ++a;};  
  4. }  
  5. f()(); f()();//都会返回2,因为调用了两个f。  
  6. var c= f();  
  7. c();c();//返回2,3,因为他们的scope chain是一样的。  



 

一个经典的错误:

 

[javascript]  view plain  copy
 
  1. function f(){  
  2.      var funs = [];  
  3.      for(var i=0;i<10;i++){  
  4.           funs.push(function(){return i;});;  
  5.      }  
  6.      return funs;  
  7. }  
  8. var funs = f();  
  9. funs[5]();     //总是返回10  



 

因为10个fun共享同一个i,当函数f调用结束后,i == 10;

正确的做法是,利用闭包构造多个不同的scope chain,这样每一个fun都有自己独立的scope chain。

正确的写法如下:

 

[javascript]  view plain  copy
 
  1. function f(){  
  2.      var funs = [];  
  3.      for(var i=0;i<10;i++){  
  4.           (function(x){  
  5.                funs.push(function(){return x;});;  
  6.           })(i);  
  7.      }  
  8.      return funs;  
  9. }  
  10. var funs = f();  
  11. funs[5]();     //总是返回10  



 

 

闭包可以用作产生block作用域,因为js是函数作用域的。严格来说这不算闭包的特性。

(function(){

//put your code here.

}());

注意最外层的()不要省略了,省略之后就是函数定义,加上()之后才是函数表达式。

 

利用闭包还可以实现bind函数,将一个函数的this绑定到一个对象上:

 

[javascript]  view plain  copy
 
  1. function bind(f,o){  
  2.      return function(){  
  3.           f.apply(o, arguments);  
  4.      };  
  5. }  



 

 

闭包内的函数可以直接访问外部函数的作用域,但是,内部函数的this指针并不继承自外部函数,如果想使用外部函数的this,可以使用上面的bind函数,也可以直接使用闭包,一般如下:

 

[javascript]  view plain  copy
 
  1. function out(){  
  2.      var self = this;     //存储this到一个局部变量,因为内部函数可以访问这个局部变量  
  3.      function inner(){  
  4.           self.xxx;     //使用外部函数的this  
  5.      }  
  6. }  



 

 

memoization

memoization 即函数返回值的缓存。如果用同样的参数调用同一个函数多次,那么从第二次开始就可以从缓存中直接取返回结果而不需计算。此例用到了闭包,当然也可以用构造函数做。

 

[html]  view plain  copy
 
  1. function memoize(f){  
  2.      var cache = {};  
  3.      return function(){  
  4.           var key = arguments.length + Array.prototype.join.call(arguments, ",");  
  5.           if(key in cache) return cache[key];  
  6.           else return cache[key]=f.apply(this, arguments);  
  7.      };  
  8. }  



 

for in

for(var name in object) 其中每次遍历出来的是object中属性的name 而不是value。

for in 循环只能遍历出enumerable的属性,

built-in method 和 built object不能被遍历出来,例如toString等。

所有用户自定义的属性都可以被遍历出来,用户自定义的继承属性也可以被遍历出来

一般遍历属性的顺序和这些属性声明的顺序是相同的,即先声明的属性先被遍历出来。

在ECMA5标准中,用户可以自定义属性是否是enumerable

 

 

native objects, hosting objects

native objects:由ECMAScript定义的对象,如:arrays, functions, dates, regular expressions.

hosting objects: 由宿主环境定义的变量,如dom element.

 

create objects

三种方法:

1,字面量:

var a = {a:1,b:2}; 

a.__proto__ == Object.prototype;

如果key中含有特殊字符或者key是保留字,可以用引号。

2, 构造函数

var a = new Constructor(a, b);

不同于字面量,a.__proto__ == Constructor.prototype;

3, Object.create

ECMAScript5中引入的新方法

var a = Object.create(Object.prototype); //等价于var a = new Object();

创建一个对象,第一个参数是prototype,可选的第二个参数是属性。如果不设置prototype,不会从Object继承。

原理:

  1. if (typeof Object.create !== 'function') {  
  2.     Object.create = function(o) {  
  3.         function F() { }  
  4.         F.prototype = o;  
  5.         return new F();  
  6.     }; 
  7.  } 

 

 

访问属性

假设:

a={a1:"a"};

b={b1:"b"};

b.__proto__ = a;

也就是b有自己的属性b,同时继承了a中的a1属性,那么:

1,b.b1, b.a1, 能正确的获取b和a中对应的属性值

2,b.b1 = "c" 能正确的修改属性值, 但是b.a1=“c”,不是修改了a的属性,而是添加了一个b.a1属性,从而影藏了a.a1,如果在执行delete b.a1 则此时b.a1 == a.a1;

3, 如果a.a1是只读的,那么b.a1="c"会出错,既不会在b上新增一个a1属性,也无法修改a.a1的值。

总结就是:get操作会对父类进行查询,set操作不会影响父类的属性值(最多就是因为重名而使父类属性被影藏了)

 

property attribute & object attribute

在ECMAScript5标准中,可以为property配置attribute。

每个property有四个attribute:value, writable, enumerable, configurable.

如果是个accessor property,则没有value和writable, 而对应的是get和set。

 

[javascript]  view plain  copy
 
  1. Object.defineProperty(obj, "p",{  
  2.      value:1,  
  3.      writable:true,  
  4.      enumerable:true,  
  5.      configurable:true  
  6. }  
  7. );  



 

同E3中对属性的的访问,如果没有p属性,则是添加操作,此时没有定义的attribute默认为false;如果p已经存在,则是修改操作,此时未定义的attribute值保持不变。

 

每个object都有三个attribue:prototype, class, extensible

prototype:访问方法:

1,obj.constructor.prototype 

2,在firefox,safari和chrome中都可以 obj.__proto__直接读写prototype,非标准不建议使用

3,E5中可以使用Object.getPrototypeOf(obj)

class:定义了obj的type,但不是不同于typeof。在E3和E5中都没有直接方法可以访问到,只能间接地通过toString方法来读取。如果下方法利用toString来获取class。

 

[javascript]  view plain  copy
 
  1. function classof(o){  
  2.      if(o === null) return "Null";  
  3.      if(o === undefined) return "Undefined";  
  4.      return Object.prototype.toString.call(o).slice(8,-1);     //因为有些js库会污染toString方法,所以这样调用,而不是用o.toString()  
  5. }  



 

extensible:

object是否可以被add/delete property,在E3中任何对象都是可以的,在E5中可以自定义。

Object.isExtensible(o);

Object.preventExtensible(o);一旦调用此方法之后,没有方法可以再变回去。

Object.seal():同时设置extensible false,并且改对象所有property.configurable=false。注意如果property是可写的,仍然可以改变其值,只是无法删除也无法添加新的。

Object.freeze():在seal基础上同时设置所有property为read-only。

注意:上述方法都只对object本身起作用,不影响其原型链上的东西。

 

JSON & serializing object

https://github.com/douglascrockford/JSON-js/blob/master/json2.js 文件中定义了JSON.stringify和JSON.parse函数,这两个函数现在在E5中是内置函数了,chrome,firefox,safari等高级浏览器都提供了原生的JSON对象。

 

数组

array是一个特殊的object,特殊在一下三个地方

1, 其index 是一种特殊的 object property,特殊在其值只能是32bit的整数。

2,从Array.prototype继承了一些方法

3,会自动维护一个length属性。如果设置任何一个index>=length的元素,则length=index+1;如果设置length,则删除所有index>=length的元素。

除此之外,array和object没什么区别。

所以可以理解js中的array没有out of bound错误,因为查询一个大于length-1的index时,会被当做一个普通的property来对来,自然会返回undefined而不是报错。

 

对象 === 关联数组 === hashmap

在js中,对象就是关联数组(或者理解为map),其本质就是key->value的无序集合。

稀疏数组 sparse array

sparse array是指index不是0开始并连续的数组,和element为undefined的数组是不同的。

var a = new Array(5);     //没有元素,但是length为5,这是稀疏数组。

var a = {,,,,,}; //有5个元素,值为undefined, 这是普通数组,不同于稀疏数组。

 

很多数组方法对于array-like object,比如novelist都是适用的,通过Array.prototype.xxx.call(nodelist,x)的方式来调用。

所谓arra-like object,就是有index和有length属性的object,并且能用[]操作符来取值。

在E5中,string也是array-like的,不过是一个read-only array-like。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值