JS高级 一、JS的数据类型 1.基本类型 JS共有5大基本类型,分别是: 1)Undefined。他只有一个值:undefined。如果一个变量被定义但是没有给他赋值,那么这个时候系统会默认给这个变量赋值为undefined。 2)Null。它同样也只有一个值:null。他是一个引用类型,当一个准备保存对象的变量因为种种原因还没有指向一个对象的时候,可以给这个变量赋值为null,通常也是这么做的。 3)Number。数值类型,他是C#中的整型和浮点型的集合,它具体是什么类型还要看它被赋值的情况,一般来说赋值为什么类型他就为什么类型。另外注意 var num = 1.0;这个时候num是个整型,只有小数点后有确切的不为0的值的时候才是浮点型。关于Number类型还有一些其他知识点,如:NaN表示该值不是一个数字,isNaN()可以判断传入的值时候为Number类型;parseInt()可以将传入的参数转成数值类型,如果包含非数值类型字符串的话,会自动将字符串去掉,parseInt("123blue")的返回值为123,字符串blue部分被忽略,因此parseInt()方法可以理解为尝试把传入的参数转换成整型,转换不了的话会将转换不了的部分忽略。parseFloat()方法与parseInt()方法类似。 4)Boolean。布尔类型,类似C#中bool类型,有true和false两个取值,但是没有类似C#中0对应False,1对应true这样的与数字的对应关系。 5)String。字符串类型,保存的是0到16位的UNICODE代码组成的字符序列。 2.复杂类型 1)Object类型。是JS的顶级“父类”(因为JS中没有类的概念,说他是父类只是为了理解上的方便),是一组数据和方法(功能)的集合,但它不具备传统面向对象语言所支持的类和接口。Object类型本质是一个无序的键值对列表,类似于集合,json格式。它包含7个方法,分别是: constructor() 构造函数? hasOwnProperty(propertyName) 检查属性是否在当前对象中 isPrototypeOf(object) 检查对象是不是该对象原型 propertyIsEnumerable(propertyName) 检查属性是否能用for-in来循环 toString() valueOf() 创建Object可以有多种方式: 通过new 关键字:var s = new Object(); s.name="james"; s.age=27; 通过JS的简单定义方式:var s = {}; s.name="james"; s.age=27; 对象字面量表示法:var s = {"name":"james","age":"27"};PS:json格式数据的key可以不用双引号括起来,value如果不是字符串的话也可以不用双引号括起来,但是推荐key和value都要用双引号括起来,以避免不必要的麻烦。 访问Object对象的属性的方法: s.name 直接点出来。 s["name"];使用方括号(类似于索引器)的优点是可以通过变量来动态访问属性:var proName="name"; alert(s[proName]); 2)Array类型。是数据的有序列表 与其他数组的不同: 数组元素可以是任何类型,同一个数组的元素类型也可以不一样,相当于C#中的List 长度可以任意改变 数组的length属性可读写(可以利用这一点删除数组元素) 数组的栈方法 后进先出 push() 往里加 pop() 从栈头往外拿,拿出来以后数组中的元素数量就会发生变化 数组的队列方法 先进先出 shift() 从队列尾部往外拿 unshift() 从队列尾部往里加 排序 sort() 排正序,将数组中的数据按照一定的顺序排列,参数可以传递一个匿名方法(类似于接口) reverse() 翻转排列 连接数组 concat() 示例:var colors=["a","b"];var newcolor=colors.concat("yellow",["c","d"]);结果colors有了5个元素。concate方法中若传入数组,会把该数组拆分,将其中元素加入目标数组中。若传入json格式数据,一个json数据就看成一个元素加入数组。 3)Function类型。函数是对象,函数名是指针 声明方式(3种): function sum(x,y){return x+y;} var sum=function(x,y){return x+y;}//函数表达式 var sum=new Function("x","y","return x+y;");//对象创建,不推荐(解析两次) Function类型没有重载。Function类型本质上就是一个数据类型,和其他类型一样,多次赋值的时候,后一次赋值会覆盖(替代)前一次赋值。多个同名函数其实就是对同一个函数对象赋值,后一个赋值会覆盖前面的赋值,所以执行的都是最后一次的函数定义。 讲Function的属性之前需要先了解JS代码的执行环境和作用域以及其他一些知识点: a)执行环境:就是当前函数(方法)所处在的父环境。比如在window下执行的函数他的执行环境就是window。真正的全局执行环境是Global,只不过大多数浏览器不公开代码访问,只是通过window来间接访问。 b)if等语句没有用块状作用域,JS的作用域与C#的不同,if,for等用大括号括起来的代码并不能形成一个块状作用域。 c)用var声明变量时,会他把添加到最近的可用环境,若不用var则把他添加的父环境中,这就理解了不用var定义的一个变量是全局变量的原因。 d)声明语句会首先执行,不管你把它放到哪里。虽然JS代码是从上到下顺序执行的,但是遇到声明的语句,编译器就会先执行声明语句,以保证其他语句执行的过程中不会因为遇到没有声明的变量而报错。 e)垃圾回收。将一个保存对象的变量设置为null,相当于切断了变量(栈)和引用值(堆)之间的关系,垃圾回收站就会自动回收 Function的内部属性: arguments 他是一个数组,保存传入的参数。 callee是一个指针,保存拥有这个arguments对象的函数对象,也就是这个函数的堆地址,当函数需要调用自己的时候,可以使用callee,不用出现自己的函数名,从而降低耦合度。 this 指向当前函数所在的执行环境,也就是函数在执行时所处的作用域 函数对象的属性和方法 length 函数定义的命名参数的个数 函数名.length prototype(原型) 保存它们所有实例方法的真实所在 apply([要改变的作用域]) 改变该函数对象的作用域 也就是改变this的值 示例代码
function sum(x, y) { alert(this); return x + y; } //window.sum(1,2); function callS() { callS.callSum1(1, 2); } callS.callSum1 = function (x, y) { alert(this); var s = sum.apply(this, arguments); //sum(1, 2); return s; } callS();
call()方法与apply()方法类似 以上两个方法不是继承而来的,可以扩充函数赖以存在的作用域,这样做最大的好处是对象和方法不需要有任何耦合关系。两者传入的第一个参数都是要改变成的作用域,第二个参数apply传入的是一个参数数组,call传入的是每一个命名参数。 二、值类型和引用类型 讲了这么多基本类型(除null以外都是值类型)和复杂类型(基本上都是引用类型),我们需要了解语言设计者为什么要设计值类型和引用类型。两者的区别是什么? 1.值类型内容长度固定,保存值的存在范围引用类型内容长度不固定,可以存储不定长度的数据; 2.值类型只能单纯的储存值,如整型,字符串等。而引用类型可以储存对象的堆地址,可以使多个变量指向同一个对象; 3.也是最主要的一点,引用类型可以缓解栈的存储压力(值类型储存在栈中)。 4.在JS语法中基本数据类型不能动态添加属性而引用数据类型可以动态添加属性。 三、检测类型的关键字 1.typeof 判断基本类型的类型 返回true or false 2.instanceof 判断复杂(引用)类型的类型 返回true or false 如果用它判断基本类型 则永远返回false 四、eval()方法 eval()方法相当强大,他就相当于一个解析器。它只接受一个参数,就是要执行的JS代码字符串。当解析器发现eval()时,他会把eval()中的参数解析出来,插入到eval执行的位置,效果相当于在相应的位置直接写入JS代码。 五、创建对象 1)简单工厂模式 2)构造函数模式。每一个实例都包括所有的方法,这样会浪费内存。 3)原型模式。把方法保存在原型中,这样所有实例可以调用这个方法,而不用每个实例都要保存这个方法