JavaScript总结

         JavaScript 是一种面向对象的动态语言,它包含类型、运算符、标准内置( built-in)对象和方法。它的语法来源于 Java 和 C,所以这两种语言的许多语法特性同样适用于 JavaScript。需要注意的一个主要区别是 JavaScript 不支持类,类这一概念在 JavaScript 通过对象原型(object prototype)得到延续(有关 ES6 类的内容参考这里Classes)。另一个主要区别是 JavaScript 中的函数也是对象,JavaScript 允许函数在包含可执行代码的同时,能像其他对象一样被传递。
    • Number(数字)
    • String(字符串)
    • Boolean(布尔)
    • Symbol(符号)(第六版新增)
    • Object(对象) Function(函数)
    • Array(数组)
    • Date(日期)
    • Math(数学对象)
    • RegExp(正则表达式)
    • Null(空)
    • Undefined(未定义)
    • Error(错误)类型 


一 、number详解
Javascript中采用全浮点计算(双精读64位)
注意 !在某些运算中会出错   eg:0.1 + 0.2 = 0.30000000000000004
我们也可以使用JavaScript提供的Math内置对象,和 内置函数parseInt();
此外JavaScript还提供了parseFloat(),但是它只支持解析十进制数字
在parseInto("123",10)  //123      -->10的含义是十进制
    
    
    
    
      
      
      
      
parseInt("010", 10); //10
parseInt("010"); // 8 如果没有第二个参数,那么以0开头就是八进制 0x开头就是16进制
parseInt("0x10"); // 16
如何把二进制转为整数值?
parseInt("10011",2);
当第一个参数不是数字型字符串 parseInt("hello", 10); // NaN
NaN是一个特殊值 用它与任何数字计算 结果都是NaN
此外  还有两个特殊值  Infinity  和 -Infinity  (正无穷和负无穷)
用内置函数  isFinite()可以判断一个变量是否为Infinity   -Infinity    NaN

二、字符串
JavaScript中的字符串是一个Unicode字符序列
每一个编码单元由一个 16 位二进制数表示。每一个Unicode字符由一个或两个编码单元来表示。
    
    
    
    
      
      
      
      
"hello".charAt(0); // "h"
"hello, world".replace("hello", "goodbye"); // "goodbye, world"
"hello".toUpperCase(); // "HELLO"
"hello".length; // 5
三、其他类型(null 和 undefined)
null的本质是一个空值,必须使用null关键字才能访问
undefined是一个未定义类型的对象(也是一个对象),它表示一个未初始化的值,也就是还没有被分配值。(JavaScript允许声明变量但不对其赋值,一个未被赋值的变量就是undefined类型)
布尔型:(true / false)其他类型对其转化
flase,0,"",NaN,null,undefined都会在JavaScript需要一个布尔值变量的时候隐式转换为false (其他的都会转化为true)
四、变量
在JavaScript中声明一个变量用var
var a
var name = "simon";
在JavaScript中的语句块中 是没有作用域的
五、运算符
JavaScript的算术操作符包括 +-*/% ——求余(与模运算不同)。赋值使用 = 运算符,此外还有一些复合运算符,如 +=-=,它们等价于 x = x op y
+可以用来连接字符串
     
     
     
     
       
       
       
       
"3" + 4 + 5; // 345
3 + 4 + "5"; // 75
1 === true; //false
123 === "123"; // false
123 == "123" // true
1 == true; // true
六、控制结构
     
     
     
     
  • if()...else if()...else()
  • while()
  • do...while()
  • for( ; ; )/for( : )
      
      
      
      
  1. 短路与:var name = o && o.getName();
  2. 短路或:var name = otherName || "default";
  3. 三元运算符:var allowed = (age > 18) ? "yes" : "no";
  4. 多重分支时可以使用  基于一个数字或字符串的switch 语句:(switch 的表达式和 case 的表达式是使用 === 严格相等运算符进行比较的:
      
      
      
      
        
        
        
        
switch(action) {
case 'draw':
drawIt();
break;
case 'eat':
eatIt();
break;
default:
doNothing();
}

      
      
      
      
        
        
        
        
switch(1 + 3){
case 2 + 2:
yay();
break;
default:
neverhappens();
}
七、对象
对象的创建方式:
1.var obj = new Object();
2.var obj = {} --->对象字面量
    
    
    
    
      
      
      
      
var obj = {
name:"carrot",
"for":"Max",
details:{
color:"orange",
size:12
}
}
  • 可以用链式访问:
               obj.details.color; //orange
  • 可以用中括号访问:
               obj[detail][color];
    
    
    
    
对象原型:(像是Java中的一个类)
     
     
     
     
function Person(name,age){
     
     
     
     
    this.name = name;
     
     
     
     
    this.age = age;     
     
     
     
     
}
    
    
    
    
创建一个Person的对象:
     var You = new Person("zhangjiahao","23");
可用You.name="ZJH"来赋值
可用var name = You.name;来取值
obj["name"] = "Simon";
var name = obj["name"];
            中括号的方式的优点:中括号内都是字符串,所以可以用于运行时计算,且可以使用关键字,但在后期被解释时无法优化。
obj.for = "Simon"; // 语法错误,因为 for 是一个预留关键字
obj["for"] = "Simon"; // 工作正常
八、数组
JavaScript中的数组是一个特殊的对象。
与普通对象的区别在于,对于数组中的‘属性’(元素)只能通过中括号来访问。同时数组比普通对象多了一个length属性,该属性值始终比最大索引大一(并非等于数组中的元素个数)
数组的创建:
var a = ['dog','cat','hen']; //数组字面量
var b = new Array('dog','cat','hen'); //传统方式
如果访问了一个不存在的数组索引 会得到undefined
数组的遍历:
for(var i=0;i<a.length;i++) //传统方法--->不推荐,每次循环都会计算length,效率不高
for(var i=0,len = a.length;i<len;i++ ) //改进方法--->先循环开始前,先缓存a.length
for(var i=0,item;item=a[i++]) //有局限,每次循环都会判断item是否空值或是否定义(是否为真)但是在数组内容中出现假值 会影响循环
for(var i in a) //这种循环 不单单会遍历出元素 ,也会遍历出自定义添加的array属性(Array.prototype 添加了新的属性
ECMAScript 5 中添加了forEach()方法(IE暂不支持)
    
    
    
    
      
      
      
      
//Array.forEach implementation for IE support..
//https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/forEach
if (!Array.prototype.forEach) {
Array.prototype.forEach = function(callback, thisArg) {
var T, k;
if (this == null) {
throw new TypeError(" this is null or not defined");
}
var O = Object(this);
var len = O.length >>> 0; // Hack to convert O.length to a UInt32
if ({}.toString.call(callback) != "[object Function]") {
throw new TypeError(callback + " is not a function");
}
if (thisArg) {
T = thisArg;
}
k = 0;
while (k < len) {
var kValue;
if (k in O) {
kValue = O[k];
callback.call(T, kValue, k, O);
}
k++;
}
};
}
还有a.push(item)方法用于在数组后面添加元素
a.push(item);

     
     
     
     
方法名称 描述
a.toString() 返回一个包含数组中所有元素的字符串,每个元素通过逗号分隔。
a.toLocaleString() 根据宿主环境的区域设置,返回一个包含数组中所有元素的字符串,每个元素通过逗号分隔。
a.concat(item1[, item2[, ...[, itemN]]]) 返回一个数组,这个数组包含原先 aitem1、item2、……、itemN 中的所有元素。
a.join(sep) 返回一个包含数组中所有元素的字符串,每个元素通过指定的 sep 分隔。
a.pop() 删除并返回数组中的最后一个元素。
a.push(item1, ..., itemN) item1、item2、……、itemN 追加至数组 a
a.reverse() 数组逆序(会更改原数组 a)。
a.shift() 删除并返回数组中第一个元素。
a.slice(start, end) 返回子数组,以 a[start] 开头,以 a[end] 前一个元素结尾。
a.sort([cmpfn]) 依据 cmpfn 返回的结果进行排序,如果未指定比较函数则按字符顺序比较(即使元素是数字)。
a.splice(start, delcount[, item1[, ...[, itemN]]]) start 开始,删除 delcount 个元素,然后插入所有的 item
a.unshift([item]) item 插入数组头部,返回数组新长度(考虑 undefined)。
九、函数
对JavaScript的学习最重要的就是对象和函数两个部分。(函数也是对象)
函数:

    
    
    
    
      
      
      
      
function add(x,y){
var total = x + y;
return total;
}
一个JavaScript函数可以有0个或者多个变量,如果有return,个返回return的值;否则返回undefined。
在使用函数时,函数中的变量也不一定强制与定义函数时的变量一致,如: add ( ) ;此时x,y为undefined;若add(1,2,3)此时3将会被忽略。
JavaScript 允许你创建匿名函数:
       
       
       
       
var avg = function() {
var sum = 0;
for (var i = 0, j = arguments.length; i < j; i++) {
sum += arguments[i];
}
return sum / arguments.length;
};

    
    
    
    
闭包:
       
       
       
       
var a = 1;
var b = 2;
(function() {
var b = 3;
a += b;
})();
a; // 4
b; // 2
递归:

    
    
    
    
      
      
      
      
function countChars(elm) {
if (elm.nodeType == 3) { // 文本节点
return elm.nodeValue.length;
}
var count = 0;
for (var i = 0, child; child = elm.childNodes[i]; i++) {
count += countChars(child);
}
return count;
}
闭包递归:
       
       
       
       
var charsInBody = (function counter(elm) {
if (elm.nodeType == 3) { // 文本节点
return elm.nodeValue.length;
}
var count = 0;
for (var i = 0, child; child = elm.childNodes[i]; i++) {
count += counter(child);
}
return count;
})(document.body);
如上所提供的函数表达式的名称的作用域仅仅是该函数自身。这允许引擎去做更多的优化,并且这种实现更可读、友好。该名称也显示在调试器和一些堆栈跟踪中,节省了调试时的时间。
ps:需要注意的是 JavaScript 函数是它们本身的对象——就和 JavaScript 其他一切一样——你可以给它们添加属性或者更改它们的属性,这与前面的对象部分一样。
十、自定义对象
首先我们了解一些面向对象的基本概念和知识。
JavaScript
    命名空间:注意:需要认识到重要的一点是:与其他面向对象编程语言不同的是,Javascript中的普通对象和命名空间在语言层面上没有区别。这点可能会让JavaScript初学者感到迷惑。
    
    
    
    
      
      
      
      
var MYAPP = MYAPP || {}; //全局命名空间
       
       
       
       
MYAPP.event = {}; //子命名空间
下面是用于创建命名空间和添加变量,函数和方法的代码写法:
         
         
         
         
//给普通方法和属性创建一个命名空间
MYAPP.commonMethod = {
regExForName = "",
regExForPhone = "",
validateName = function(name){
//对参数name做一些操作
},
validatePhoneNo = function(phoneNo){
//对参数phoneNo做一些操作
}
}
 
MYAPP.event = {
addListener = function(el , type , fn){
//code
}
removeListener = function(el , type , fn){
//code
}
}
 
//使用addListner方法的写法
MYAPP.event.addListener("yourel" , "type" , callback)

自定义对象
JavaScript是一种基于原型的语言,它并没有类似于其他C++/JAVA中的类声明语言。在JavaScript中,我们通常使用方法(function)来当做类。定义一个类跟定义一个函数一样easy
       
       
       
       
function Person(){}
//或
var Person = function(){}

对象(类的实例)
我们使用 new obj()来创建一个新的实例。
eg:
       
       
       
       
function Person(){}
var person1 = new Person();
var person2 = new Person();

构造器
在实例化时构造器被调用,在JavaScript中,初始的函数就可以作为构造器使用,而不需要额外的定义构造器方法。类中的每个声明的函数都可以在被实例化后调用执行。(构造器常用于给对象的属性赋初始值)
       
       
       
       
function Person() {
alert('Person instantiated');
}
 
var person1 = new Person();
var person2 = new Person();

属性(对象属性)
属性就是当前类中的变量。通常为了正确继承,属性应当为定义在原型属性中。
可以使用关键字this来调用类中的属性,this是对当前对象的引用。
       
       
       
       
function Person(name){
this.name = name;
alert("this is my name" + this.name);
}
var person1 = new Person("Alice");
var person2 = new Person("Bob");
方法(对象属性)
方法和属性相似,不同的是:一个是函数,另一个是可以被定义为函数。为定义一个方法,通常需要将一个函数赋值给类的原型属性,这个赋值给函数的名称就是用来给对象在外部调用它使用的。
      
      
      
      
function Person(name){
this.name = name;
}
Person.protopype.sayHello = function(){
alert("hello,i'm" + this.name);
}
var person1 = new Person("Alice");
var person2 = new Person("Bob");
person1.sayHello();
在JavaScript中方法通常是绑定到一个对象中的普通函数,所以我们也可以在访问域之外对其调用访问。
       
       
       
       
function Person(name){
this.name = name;
}
Person.protopype.sayHello = function(){
alert("hello,i'm" + this.name);
}
var person1 = new Person("Alice");
var person2 = new Person("Bob");
person1.sayHello(); //Alice
 
var helloFunction = person1.sayHello;
helloFunction(); //undefined
 
console.log(helloFunction === person1.sayHello); // logs true
console.log(helloFunction === Person.prototype.sayHello); // logs true
helloFunction.call(person1);

继承
创建一个或者多个类的某专门版本类方式称为继承( JavaScript只支持单继承)。 创建的专门版本的类通常称作子类。在JavaScript中通过赋予子类一个父类的实例并专门化子类来实现。
       
       
       
       
function Person(name){
this.name = name;
}
 
Person.prototype.walk = function(){
alert("i'm walking!");
}
 
Person.prototype.sayHello = function(){
alert("Hello , I'm" + this.name );
}
//定义Student构造器
function Student(name , subject){
Person.call(this , name);//这里也可以用apply(),详细见总结末端
this.subject = subject;
}
 
//建立一个由Person.prototype继承而来的Student.prototype对象
// 注意: 常见的错误是使用 "new Person()"来建立Student.prototype.
// 这样做的错误之处有很多, 最重要的一点是我们在实例化时
// 不能赋予Person类任何的FirstName参数
// 调用Person的正确位置如下,我们从Student中来调用它
Student.prototype = Object.create(Person.prototype);
 
//设置constructor 属性指向Student
Student.prototype.constructor = Student;
 
Student.prototype.sayHello = function(){
console.log("hello , I'm" + this.name + "I'm studying" + this.subject);
}
Student.prototype.sayGoodBye = function(){
alert("GOODBYE");
}
// 测试实例:
var student1 = new Student("Janet", "Applied Physics");
student1.sayHello(); // "Hello, I'm Janet. I'm studying Applied Physics."
student1.walk(); // "I am walking!"
student1.sayGoodBye(); // "Goodbye!"
 
// Check that instanceof works correctly
console.log(student1 instanceof Person); // true
console.log(student1 instanceof Student); // true
 
 
 
 
封装

在上一个例子中,Student类虽然不需要知道Person类的walk()方法是如何实现的,但是仍然可以使用这个方法;Student类不需要明确地定义这个方法,除非我们想改变它。 这就叫做封装,对于所有继承自父类的方法,只需要在子类中定义那些你想改变的即可。

抽象
抽象是允许模拟工作问题中通用部分的一种机制。这可以通过继承或者组合来实现。
JavaScript通过继承来实现具体化,通过让类的实例是其他对象的属性值来实现组合。
    
    
    
    
      
      
      
      
var foo = function(){};
console.log( 'foo is a Function: ' + (foo instanceof Function) ); // logs "foo is a Function: true"
cpnsole.log( 'foo.prototype is an Object: ' + (foo.prototype instanceof Object) ); // logs "foo.prototype is an Object: true"

多态

就像所有定义在原型属性内部的方法和属性一样,不同的类可以定义具有相同名称的方法;方法是作用于所在的类中。并且这仅在两个类不是父子关系时成立(继承链中,一个类不是继承自其他类)。

---------------------------------------------------------------------------------
总结:
    在经典面向对象中,对象指的是数据以及操作数据的集合。与C++ 和 Java 不同,JavaScript是一种基于原型的编程语言,没有Class语句,而是把函数作为类。
eg:(a)
    
    
    
    
      
      
      
      
function makePerson(first , last){
return {
first : first,
last : last
}
}
function personFullName(person){
return person.first + " " + person.last;
}
function personFullNameReversed(person){
return person.last + " " + person.first;
}
person1 = makePerson("Kobe","Branyt");
personFullName(person1);
personFullNameReversed(person1);
显然上述方法十分麻烦,需要在全局命名空间中写很多的函数。既然函数本身就是对象,如果需要让一个函数
属于一个对象,那么不难得到:(b)
    
    
    
    
      
      
      
      
function makePerson(first , last){
return{
first : fitst,
last : last,
fullName function(){
return this.first + ' ' this.last;
}
}
}
s = makerPerson('Kobe','Branty');
s.fullName();// Kobe Branty
注意:this关键字, 当使用在函数中时,this指的是当前的对象,也就是调用该函数的对象。如果一个对象使用了.或者{}来访问其下的属性时,那么该对象就成了this。如果并不存在某个对象,那么此时this指向全局对象。
    
    
    
    
      
      
      
      
s = makePerson('Allen','Iverson');
var fullName = s.fullName;
fullName(); //undefined undefined
所以我们可以用this来改进上述makePerson函数:
    
    
    
    
      
      
      
      
function Person(first, last) {
this.first = first;
this.last = last;
this.fullName = function() {
return this.first + ' ' + this.last;
}
this.fullNameReversed = function() {
return this.last + ', ' + this.first;
}
}
var s = new Person("Simon", "Willison");
但此时创建一个Person对象的时候,我们都在其中创建了两个新的函数对象。所以我们可以将里面的函数拿出来:
       
       
       
       
function personFullName(person) {
return this.first + ' ' + this.last;
}
function personFullNameReversed(person) {
return this.last + ', ' + this.first;
}
function Person(first, last) {
this.first = first;
this.last = last;
this.fullName = personFullName;
this.fullNameReversed = personFullNameReversed;
}
最好的方式:
       
       
       
       
function Person(first, last) {
this.first = first;
this.last = last;
}
Person.prototype.fullName = function() {
return this.first + ' ' + this.last;
}
Person.prototype.fullNameReversed = function() {
return this.last + ', ' + this.first;
}
Person.porotype 是一个可以被Person的所有实例共享的对象。他的名字叫做原型链(prototype chain)的查询链的一部分:当你试图访问一个Person中没有定义的属性时,解释器会先检查这个Person.porotype来判断是否存在这样一个属性。所以,在任何时候任何分配给Person.porotype的东西通过this对象构造的实例都是有用的。
这也使得我们可以在任何时候改造原型中的一些东西,甚至是内置对象的原型。 String 添加一个方法用来返回逆序的字符串:
      
      
      
      
var s = "Simon";
s.reversed(); // TypeError on line 1: s.reversed is not a function
 
String.prototype.reversed = function() {
var r = "";
for (var i = this.length - 1; i >= 0; i--) {
r += this[i];
}
return r;
}
s.reversed(); // nomiS
原型组成链的一部分,那条链的根节点是Object.pototype,它包括toString()方法 --> 将对象转换成字符串调用的方法。这对于调用我们的Person对象很有用。
       
       
       
       
var s = new Person("Simon", "Willison");
s; // [object Object]
 
Person.prototype.toString = function() {
return '<Person: ' + this.fullName() + '>';
}
s.toString(); // <Person: Simon Willison>
ps:apply()和call()
       
       
       
       
function trivialNew(constructor , ...args){ (...args是剩余参数2015新特性)
var o = {};//创建一个新对象
constructor.apply(o,args);
return o;
}
apply() 有一个姐妹函数,名叫 call,它也可以允许你设置 this,但它带有一个扩展的参数列表而不是一个数组。
       
       
       
       
function lastName(){
return this.last.toUpperCase();
}
var s = new Person("Simon","Willison");
lastName.call(s);
//与下面效果一致
s.lastName = lastName;
s.lastName();

内部函数

JavaScript 允许在一个函数内部定义函数,如果某个函数依赖于其他的一两个函数,而这一两个函数对其余的代码没有用处,可以将它们嵌套在会被调用的那个函数内部,这样做可以减少全局作用域下的函数的数量,这有利于编写易于维护的代码。
       
       
       
       
function betterExampleNeeded() {
var a = 1;
function oneMoreThanA() {
return a + 1;
}
return oneMoreThanA();
}


闭包
闭包是JavaScript中必须提到的功能。首先我们来看一个内部函数应用的例子:
       
       
       
       
function makeAdder(a){
return funtion(b){
return a+b;
}
}
var x = makeAdder(5);
var y = makeAdder(20);
x(6);//11
y(7);//27
我们来观察一下,在上述代码运行过程中发生了什么:每当JavaScript执行一个函数时,都会创建一个作用域对象(Scopeobject),用来保存 在这个函数中创建的局部变量。它和被传入函数的变量一起被初始化。这与那些保存的所有全局变量和函数的全局对象类似,但有一些区别:
1.每次函数被执行时都会创建一个新的,特定的作用域对象;
2.与全局对象(在浏览器中当做window对象来访问的)不同的是,不能从JavaScript代码中直接访问作用域对象,也没有可以遍历当前的作用域对象里面属性的方法。

过程:当调用makeAdder时,解释器创建了一个作用域对象,它带有一个属性a(它被当做参数传入makeAdder函数中,然后makeAdder返回一个新创建的函数)。通常JavaScript的垃圾回收器会在这时候回收makeAdder创建的作用域对象,但是返回的函数却保留了一个指向那个作用域对象的引用。结果是这个作用域对象并不会被垃圾回收器回收。直到指向makeAdder 的那个函数对象的引用计数为零。

作用域对象组成了一个名为作用域链(scope chain)的链.它类似于原形(prototype)链一般,被JavaScript的对象系统使用。

那么 : 一个闭包就是一个函数 和 在这个函数中被创建函数的作用域对象的组合。

闭包允许你保存状态--所以他们通常可以替代对象来使用。

内存泄漏
使用闭包的一个缺点就是容易造成浏览器内存泄漏。JavaScript 是一种具有垃圾回收机制的语言——对象在被创建的时候分配内存,然后当指向这个对象的引用计数为零时,浏览器会回收内存。宿主环境提供的对象都是按照这种方法被处理的。
IE 浏览器有自己的一套垃圾回收机制,这套机制与 JavaScript 提供的垃圾回收机制进行交互时,可能会发生内存泄露。
       
       
       
       
function leakMemory() {
var el = document.getElementById('el');
var o = { 'el': el };
el.o = o;
}
在 IE 中,每当在一个 JavaScript 对象和一个本地对象之间形成循环引用时,就会发生内存泄露。这段代码的循环引用会导致内存泄露:IE 不会释放被 elo 使用的内存,直到浏览器被彻底关闭并重启后。
       
       
       
       
function addHandler() {
var el = document.getElementById('el');
el.onclick = function() {
el.style.backgroundColor = 'red';
}
}
解决方式





























JavaScript总结_第1张图片



你可能感兴趣的:(JavaScript,闭包)