0%

面向对象的语言有一个标志,即拥有类的概念,抽象实例对象的公共属性与方法,基于类可以创建任意多个实例对象,一般具有封装、继承、多态的特性!但JS中对象与纯面向对象语言中的对象是不同的,ECMA标准定义JS中对象:无序属性的集合,其属性可以包含基本值、对象或者函数。可以简单理解为JS的对象是一组无序的值,其中的属性或方法都有一个名字,根据这个名字可以访问相映射的值(值可以是基本值/对象/方法)。

一、理解对象:

  1. 第一种:基于Object对象

     var person = new Object();
     person.name = 'My Name';
     person.age = 18;
     person.getName = function(){
         return this.name;
     }
    
  2. 第二种:对象字面量方式(比较清楚的查找对象包含的属性及方法)

     var person = {
         name : 'My name',
         age : 18,
         getName : function(){
         return this.name;
         }
     }
    

    JS的对象可以使用‘.’操作符动态的扩展其属性,可以使用’delete’操作符或将属性值设置为’undefined’来删除属性。如下:

     person.newAtt=’new Attr’;//添加属性
     alert(person.newAtt);//new Attr
     delete person.age;
     alert(person.age);//undefined(删除属性后值为undefined);
    

    二、对象属性类型

    ECMA-262第5版定义了JS对象属性中特征(用于JS引擎,外部无法直接访问)。ECMAScript中有两种属性:数据属性和访问器属性

1.数据属性:

数据属性指包含一个数据值的位置,可在该位置读取或写入值,该属性有4个供述其行为的特性:

  • [[configurable]]:表示能否使用delete操作符删除从而重新定义,或能否修改为访问器属性。默认为true;

  • [[Enumberable]]:表示是否可通过for-in循环返回属性。默认true;

  • [[Writable]]:表示是否可修改属性的值。默认true;

  • [[Value]]:包含该属性的数据值。读取/写入都是该值。默认为undefined;如上面实例对象person中定义了name属性,其值为’My name’,对该值的修改都反正在这个位置。

    要修改对象属性的默认特征(默认都为true),可调用Object.defineProperty()方法,它接收三个参数:属性所在对象,属性名和一个描述符对象(必须是:configurable、enumberable、writable和value,可设置一个或多个值)。

    如下:(浏览器支持:IE9+、Firefox 4+、Chrome、Safari5+)

    var person = {};
    Object.defineProperty(person, ‘name’, {
    configurable: false,
    writable: false,
    value: ‘Jack’
    });
    alert(person.name);//Jack
    delete person.name;
    person.name = ‘lily’;
    alert(person.name);//Jack

可以看出,delete及重置person.name的值都没有生效,这就是因为调用defineProperty函数修改了对象属性的特征;值得注意的是一旦将configurable设置为false,则无法再使用defineProperty将其修改为true(执行会报错:can’t redefine non-configurable property);

2.访问器属性:

它主要包括一对getter和setter函数,在读取访问器属性时,会调用getter返回有效值;写入访问器属性时,调用setter,写入新值;该属性有以下4个特征:

  • [[Configurable]]:是否可通过delete操作符删除重新定义属性;
  • [[Numberable]]:是否可通过for-in循环查找该属性;
  • [[Get]]:读取属性时调用,默认:undefined;
  • [[Set]]:写入属性时调用,默认:undefined;

访问器属性不能直接定义,必须使用defineProperty()来定义,如下:

var person = {
      _age: 18
};
Object.defineProperty(person, 'isAdult', {
    get: function () {
            if (this._age >= 18) {
                return true;
            } else {
                return false;
            }
    }
});
alert(person.isAdult?'成年':'未成年');//成年      

从上面可知,定义访问器属性时getter与setter函数不是必须的,并且,在定义getter与setter时不能指定属性的configurable及writable特性;

此外,ECMA-262(5)还提供了一个Object.defineProperties()方法,可以用来一次性定义多个属性的特性:

    var person = {};
    Object.defineProperties(person,{
        _age:{
        value:19
       },
        isAdult:{
            get: function () {
                if (this._age >= 18) {
                    return true;
                } else {
                return false;
                }
            }
        }
        });
        alert(person.isAdult?'成年':'未成年');//成年

上述代码使用Object.defineProperties()方法同时定义了_age及isAudlt两个属性的特性

此外,使用Object.getOwnPropertyDescriptor()方法可以取得给定属性的特性:

var descriptor = Object.getOwnPropertyDescriptor(person,'_age');
alert(descriptor.value);//19

对于数据属性,可以取得:configurable,enumberable,writable和value;

对于访问器属性,可以取得:configurable,enumberable,get和set

三、创建对象

使用Object构造函数或对象字面量都可以创建对象,但缺点是创建多个对象时,会产生大量的重复代码,因此下面介绍可解决这个问题的创建对象的方法

  1. 工厂模式

     function createPerson(name, age, job) {
     var o = new Object();
     o.name = name;
     o.age = age;
     o.job = job;
     o.getName = function () {
         return this.name;
     }
     return o;//使用return返回生成的对象实例
     }
     var person = createPerson('Jack', 19, 'SoftWare Engineer');
    

    创建对象交给一个工厂方法来实现,可以传递参数,但主要缺点是无法识别对象类型,因为创建对象都是使用Object的原生构造函数来完成的。

  2. 构造函数模式

     function Person(name,age,job){
         this.name = name;
         this.age = age;
         this.job = job;
         this.getName = function () {
             return this.name;
         }
     }
     var person1 = new Person('Jack', 19, 'SoftWare Engineer');
    
     var person2 = new Person('Liye', 23, 'Mechanical Engineer');
    

使用自定义的构造函数(与普通函数一样,只是用它来创建对象),定义对象类型(如:Person)的属性和方法。它与工厂方法区别在于:

  • 没有显式地创建对象
  • 直接将属性和方法赋值给this对象;
  • 没有return语句;

此外,要创建Person的实例,必须使用new关键字,以Person函数为构造函数,传递参数完成对象创建;实际创建经过以下4个过程:

  • 创建一个对象
  • 将函数的作用域赋给新对象(因此this指向这个新对象,如:person1)
  • 执行构造函数的代码
  • 返回该对象

上述由Person构造函数生成的两个对象person1与person2都是Person的实例,因此可以使用instanceof判断,并且因为所有对象都继承Object,因此person1 instanceof Object也返回真:

 alert(person1 instanceof Person);//true;
 alert(person2 instanceof Person);//true;
 alert(person1 instanceof Object);//true;
 alert(person1.constructor === person2.constructor);//ture;       

虽然构造函数方式比较不错,但也存在缺点,那就是在创建对象时,特别针对对象的属性指向函数时,会重复的创建函数实例,以上述代码为基础,可以改写为:

function Person(name,age,job){
   this.name = name;
   this.age = age;
   this.job = job;
   this.getName = new Function () {//改写后效果与原代码相同,不过是为了方便理解
           return this.name;
   }
 }        

上述代码,创建多个实例时,会重复调用new Function();创建多个函数实例,这些函数实例还不是一个作用域中,当然这一般不会有错,但这会造成内存浪费。当然,可以在函数中定义一个getName = getName的引用,而getName函数在Person外定义,这样可以解决重复创建函数实例问题,但在效果上并没有起到封装的效果,如下所示:

function Person(name,age,job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.getName = getName;
 }
 function getName() {//到处是代码,看着乱!!
    return this.name;
 }        

3.原型模式

JS每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,它是所有通过new操作符使用函数创建的实例的原型对象。原型对象最大特点是,所有对象实例共享它所包含的属性和方法,也就是说,所有在原型对象中创建的属性或方法都直接被所有对象实例共享。

function Person(){
}
Person.prototype.name = 'Jack';//使用原型来添加属性
Person.prototype.age = 29;
Person.prototype.getName = function(){
   return this.name;
}
var person1 = new Person();
alert(person1.getName());//Jack
var person2 = new Person();
alert(person1.getName === person2.getName);//true;共享一个原型对象的方法   

原型是指向原型对象的,这个原型对象与构造函数没有太大关系,唯一的关系是函数的prototype是指向这个原型对象!而基于构造函数创建的对象实例也包含一个内部指针为:[[prototype]]指向原型对象。

实例属性或方法的访问过程是一次搜索过程:

  • 首先从对象实例本身开始,如果找到属性就直接返回该属性值;
  • 如果实例本身不存在要查找属性,就继续搜索指针指向的原型对象,在其中查找给定名字的属性,如果有就返回;
  • 基于以上分析,原型模式创建的对象实例,其属性是共享原型对象的;
  • 但也可以自己实例中再进行定义,在查找时,就不从原型对象获取,而是根据搜索原则,得到本实例的返回;简单来说,就是实例中属性会屏蔽原型对象中的属性;

原型与in操作符

一句话:无论原型中属性,还是对象实例的属性,都可以使用in操作符访问到;要想判断是否是实例本身的属性可以使用object.hasOwnProperty(‘attr’)来判断;

原生对象中原型

原生对象中原型与普通对象的原型一样,可以添加/修改属性或方法,如以下代码为所有字符串对象添加去左右空白原型方法:

 String.prototype.trim = function(){
    return this.replace(/^\s+/,'').replace(/\s+$/,'');
 }
 var str = '   word space   ';
 alert('!'+str.trim()+'!');//!word space!   

原型模式的缺点,它省略了为构造函数传递初始化参数,这在一定程序带来不便;另外,最主要是当对象的属性是引用类型时,它的值是不变的,总是引用同一个外部对象,所有实例对该对象的操作都会其它实例:

function Person() {
}
Person.prototype.name = 'Jack';
Person.prototype.lessons = ['Math','Physics'];
var person1 = new Person();
person1.lessons.push('Biology');
var person2 = new Person();
alert(person2.lessons);//Math,Physics,Biology,person1修改影响了person2   


4.组合构造函数及原型模式

目前最为常用的定义类型方式,是组合构造函数模式与原型模式。构造函数模式用于定义实例的属性,而原型模式用于定义方法和共享的属性。结果,每个实例都会有自己的一份实例属性的副本,但同时又共享着对方方法的引用,最大限度的节约内存。此外,组合模式还支持向构造函数传递参数,可谓是集两家之所长。

function Person(name, age, job) {
        this.name = name;
        this.age = age;
        this.job = job;
        this.lessons = ['Math', 'Physics'];
    }
    Person.prototype = {
        constructor: Person,//原型字面量方式会将对象的constructor变为Object,此外强制指回Person
        getName: function () {
            return this.name;
        }
    }
    var person1 = new Person('Jack', 19, 'SoftWare Engneer');
    person1.lessons.push('Biology');
    var person2 = new Person('Lily', 39, 'Mechanical Engneer');
    alert(person1.lessons);//Math,Physics,Biology
    alert(person2.lessons);//Math,Physics
    alert(person1.getName === person2.getName);//true,//共享原型中定义方法    

在所接触的JS库中,jQuery类型的封装就是使用组合模式来实例的!!!

5.动态原型模式

组合模式中实例属性与共享方法(由原型定义)是分离的,这与纯面向对象语言不太一致;动态原型模式将所有构造信息都封装在构造函数中,又保持了组合的优点。其原理就是通过判断构造函数的原型中是否已经定义了共享的方法或属性,如果没有则定义,否则不再执行定义过程。该方式只原型上方法或属性只定义一次,且将所有构造过程都封装在构造函数中,对原型所做的修改能立即体现所有实例中:

function Person(name, age, job) {
            this.name = name;
            this.age = age;
            this.job = job;
            this.lessons = ['Math', 'Physics'];
        }
        if (typeof this.getName != 'function') {//通过判断实例封装
        Person.prototype = {
            constructor: Person,//原型字面量方式会将对象的constructor变为Object,此外强制指回Person
            getName: function () {
                return this.name;
            }
            }
        }
    var person1 = new Person('Jack', 19, 'SoftWare Engneer');
    person1.lessons.push('Biology');
    var person2 = new Person('Lily', 39, 'Mechanical Engneer');
    alert(person1.lessons);//Math,Physics,Biology
    alert(person2.lessons);//Math,Physics
    alert(person1.getName === person2.getName);//true,//共享原型中定义方法           

js回调函数的理解

在认识js回调函数之前,首先要了解“函数也是一种数据类型”,它也可以像变量一样使用

1、var a = function(){console.log("function a");}

2、function a(){console.log("function a");}

以上两种函数定义方式最终效果是一样的

举个例子:

function a(){console.log("function a");}

var b = a;//此时将function a赋值给b

b();//执行b函数输出“function a”

delete a;//删除a

a();//undefined

回调函数:简单通俗点就是当有a和b两个函数,当a作为参数传给b,并在b中执行,这时a就是一个回调(callback)函数,如果a是一个匿名函数,则为匿名回调函数

在使用jquery时,经常会使用到回调,例如:

1、绑定事件

$("p").blur( function () { alert("Hello World!"); } );

$("p").click( function () { $(this).hide(); });

2、效果动作

$("p").show("fast",function(){
$(this).text("Animation Done!");
});

3、ajax操作

$("#feeds").load("feeds.php", {limit: 25}, function(){
alert("The last 25 entries in the feed have been loaded");
});

如何自定义一个回调函数呢?

举个小例子:

想要执行一个加法运算操作,在加操作执行完毕,我们还想要将这个加的结果作一个2倍操作,并返回最终值

function double(data){
    return data*2;
}

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

从上面代码可知,double函数作为参数传给add函数,add函数中执行完sum=a+b;后执行了double函数,double函数就是回调函数。

我对回调函数的理解是,回调就是一个函数的调用过程。函数a有一个参数,这个参数是个函数b,当函数a执行完以后执行函数b。函数b就是回调函数。那么这个过程就叫回调。

定义函数有两种方式:函数声明和函数表达式。
Firefox、Chrome、Safari、Opera都给函数定义了一个非标准的name属性,通过这个属性可以访问到函数名。

函数声明最重要的特征就是函数声明提前(function declaration hoisting)。在执行代码之前会先读取函数声明,意味着可以把函数声明放在调用语句的后面。

var functionName = function (arg0, arg1, arg2){
    //函数体
};

创建一个函数并将它赋值给变量functionName。这种情况下创建的函数叫匿名函数(anonymous function),有时候也叫Lambda函数。匿名函数的name属性是空字符串。

函数表达式与其他表达式一样,必须先赋值后使用。

递归

function factorial(num) {
    if(num<=){
        return 1;
    } else {
        return num*factorial(num-1);
    }
}

下面的代码可能出错:

var anotherFactorial = factorial;
factorial = null;
alert(anotherFactorial(4)); //出错    

可以使用arguments.callee来解决这个问题。arguments.callee是一个指向正在执行的函数的指针。

function factorial(num) {
    if(num<=){
        return 1;
    } else {
        return num*arguments.callee(num-1);
    }
}

但在严格模式下,不能通过脚本访问arguments.callee,访问这个属性会导致错误。不过可以使用命名函数表达式来达成相同的结果。

var factorial = (function f (num) {
    if (num <= 1) {
        return 1;
    } else {
        return num * f(num-1);
    }
});

闭包

匿名函数和闭包经常混淆。闭包是指有权访问另一个函数作用域中的变量的函数。
创建闭包的常见方式,就是在一个函数内部创建另一个函数。

function createComparisonFunction (propertyName) {
    return function (object1, object2) {
        var value1 = object1[propertyName];
        var value2 = object2[propertyName];

        if (value1 < value2) {
            return -1;
        } else if (value1 > value2) {
            return 1;
        } else {
            return 0;
        }
    };
}

闭包与变量

作用域链的这种配置机制引出了一个值得注意的副作用,即闭包只能取得包含函数中任何变量的最后一个值。别忘了闭包所保存的是整个变量对象,而不是某个特殊的变量。

function createFunction () {
    var result = new Array();

    for (var i= 0; i < 10; i++) {
        result[i] = function () {
            return i;
        };
    }

    return result;
}

事实上result中的每个函数都返回10。但是,我们可以通过创建另一个匿名函数强制让闭包的行为符合预期。

function createFunction () {
    var result = new Array();

    for (var i= 0; i < 10; i++) {
        result[i] = function (num) {
            return function() {
                return num;
            };
        }(i);
    }

    return result;
}

关于this对象

在闭包中使用this对象也可能会导致一些问题。我们知道,this对象是在运行时基于函数的执行环境绑定的:在全局函数中,this等于window,而当函数被作为某个对象的方法调用时,this等于那个对象。不过,匿名函数的执行环境具有全局性,因此其this对象通常指向window。但有时候由于编写闭包的方式不同,这一点可能不会那么明显。

var name = "The Window";

var object = {
    name : "My Object",

    getNameFunc : function () {
        return function () {
            return this.name;
        };
    }
};

alert(object.getNameFunc()()); //"The Window" (在非严格模式下)

每个函数在被调用时,其活动对象都会自动取得两个特殊变量:this和arguments。内部函数在搜索这两个变量时,只会搜索到其活动对象为止,因此永远不可能直接访问外部函数中的这两个变量。不过,把外部作用域中的this对象保存在一个闭包能够访问到的变量里,就可以让闭包访问对象了。

var name = "The Window";

var object = {
    name : "My Object",

    getNameFunc : function () {
        var that = this;
        return function () {
            return that.name;
        };
    }
};

alert(object.getNameFunc()()); //"My Object"

this和arguments也存在同样的问题。如果想访问作用域中的arguments对象,必须将对象的引用保存到另一个闭包能够访问的变量中。
在几种特殊情况下,this的值可能会意外地改变。

var name = "The Window";

var object = {
    name : "My Object",
    getName : function () {
        return this.name;
    }
};

object.getName(); //"My Object"
(object.getName)(); //"My Object"
(object.getName = object.getName)(); //"The Window",在非严格模式下

内存泄露

由于IE9之前的版本对JScript对象和COM对象使用不同的垃圾收集例程,因此闭包在IE的这些版本中会导致一些特殊的问题:如果闭包的作用域链中保存着一个HTML元素,那么就意味着该元素将无法被销毁。

function assignHandler () {
    var element = document.getElementById("someElement");
    element.onclick = function () {
        alert(element.id);
    };
}

这个问题可以通过修改下代码来解决。

function assignHandler () {
    var element = document.getElementById("someElement");
    var id = element.id;    
    element.onclick = function () {
        alert(id);
    };
    element = null;
}

模仿块级作用域

私有变量

严格来讲,JavaScript中没有私有成员的概念;所有对象属性都是公有的。不过,到是有一个私有变量的概念。任何函数中定义的变量,都可以认为是私有变量。私有变量包括函数的参数、局部变量和在函数内部定义的其他函数。

闭包可以通过自己的作用域链也可以访问局部变量,利用这一点,就可以创建用于访问私有变量的公有方法。

我们把有权访问私有变量和私有函数的公有方法称为特权方法(privileged method)。有两种在对象上创建特权方法的方式:

第一种是在构造函数中定义特权方式。

    function MyObject () {
    //私有变量和私有函数
    var privateVariable = 10;

    function privateFunction () {
        return false;
    }

    //特权方法
    this.publicMethod = function () {
        privateVariable++;
        return privateFunction();
    };
}

不过,在构造函数中定义特权方法也有一个缺点,那就是你必须使用构造函数模式来达到这个目的。构造函数模式的缺点是针对每个实例都会创建同样一组新方法,而使用静态私有变量来实现特权方法就可以避免这个问题。

静态私有变量

通过在私有作用域中定义私有变量和函数,同样也可以创建特权方法。

(function () {
    //私有变量和私有函数
    var privateVariable = 10;
    function privateFunction () {
        return false;
    }

    //构造函数
    MyObject = function () {};

    //公有/特权方法
    MyObject.prototype.publicMethod = function () {
        privateVariable++;
        return privateFunction();
    };
    })();

模块模式

前面的模式是用于自定义类型创建私有变量和特权方法的。而道格拉斯所说的模块模式(module patten)则是为单例创建私有变量和方法。所谓单例(singleton),指的就是一个实例的对象。

    var signleton = function () {
        //私有变量和私有函数
        var privateVariable = 10;
    
        function privateFunction () {
            return false;
        }

        //公有/特权方法
        return {
            publicProperty : true;
            publicMethod : function() {
                privateVariable++;
                return privateFunction();
            }
        };
    }();

增强的模块模式

这种增强的模块模式适合那些单例必须是某种类型的实例,同时还必须添加某些属性和(或)方法对其加以增强的情况。

    var singleton = function(){
       // 私有变量和私有函数
       var privateVariable = 10;
       
        function privateFunction () {
            return false;
        }
        //创建对象
        var object = new CustomType();
        
        //添加特权/公有属性和方法
        object.publicProperty =true;
        
        object.publicMethod=function(){
            privateVariable++;
                return privateFunction();
        };
        
        //返回这个对象
        return object;
     }();

一. 三大方法

RegExp采用直接量语法表示:/pattern/attributes

attributes有三个选择,i、m和g,m(多行匹配)不常用直接省略,所以一个pattern(匹配模式)可以表示如下:

var pattern = /hello/ig;

i(ignore)表示不区分大小写(地搜索匹配),比较简单,以下例子中不加述说;g(global)表示全局(搜索匹配),即找到一个后继续找下去,相对复杂,以下各种方法中会特别介绍。

既然是RegExp的三大方法,所以都是pattern.test/exec/complie的格式。

  1. test

    主要功能:检测指定字符串是否含有某个子串(或者匹配模式),返回true或者false。

     示例如下:
     
     var s = 'you love me and I love you';
     var pattern = /you/;
     var ans = pattern.test(s);
     console.log(ans); // true
    

    如果attributes用了g,则可以继续找下去,其中还会涉及lastIndex属性(参照exec中搭配g的介绍)。

  2. exec

    主要功能:提取指定字符串中的符合要求的子串(或者匹配模式),返回一个数组存放匹配结果;如果没有,则返回null。(也可自己写方法循环提取所有或者指定index的数据)

    exec可以说是test的升级版本,因为它不仅可以检测,而且检测到了可以直接提取结果。

    示例如下:

     var s = 'you love me and I love you';
     var pattern = /you/;
     var ans = pattern.exec(s);
     console.log(ans); // ["you", index: 0, input: "you love me and I love you"]
     console.log(ans.index); // 0
     console.log(ans.input); // you love me and I love you
    

    输出的东西很有意思。此数组的第 0 个元素是与正则表达式相匹配的文本,第 1 个元素是与 RegExpObject 的第 1 个子表达式相匹配的文本(如果有的话),第 2 个元素是与 RegExpObject 的第 2 个子表达式相匹配的文本(如果有的话),以此类推。

    啥叫“与子表达式相匹配的文本”?看下面的例子:

     var s = 'you love me and I love you';
     var pattern = /y(o?)u/;
     var ans = pattern.exec(s);
     console.log(ans);   // ["you", "o", index: 0, input: "you love me and I love you"]
     console.log(ans.length) // 2
    

    所谓的子表达式就是pattern里()内的东西(具体可以参考下文对子表达式的介绍)。再看上面例子的数组长度,是2!!index和input只是数组属性(chrome中以上的输出可能会让人误会)。

    除了数组元素和 length 属性之外,exec() 方法还返回两个属性。index 属性声明的是匹配文本的第一个字符的位置。input 属性则存放的是被检索的字符串 string。我们可以看得出,在调用非全局的 RegExp 对象的 exec() 方法时,返回的数组与调用方法 String.match() 返回的数组是相同的。

    如果使用 “g” 参数,exec() 的工作原理如下(还是以上的例子 ps:如果test使用g参数类似):

    找到第一个 “you”,并存储其位置
    如果再次运行 exec(),则从存储的位置(lastIndex)开始检索,并找到下一个 “you”,并存储其位置

    当 RegExpObject 是一个全局正则表达式时,exec() 的行为就稍微复杂一些。它会在 RegExpObject 的 lastIndex 属性指定的字符处开始检索字符串 string。当 exec() 找到了与表达式相匹配的文本时,在匹配后,它将把 RegExpObject 的 lastIndex 属性设置为匹配文本的最后一个字符的下一个位置。这就是说,我们可以通过反复调用 exec() 方法来遍历字符串中的所有匹配文本。当 exec() 再也找不到匹配的文本时,它将返回 null,并把 lastIndex 属性重置为 0。这里引入lastIndex属性,这货只有跟g和test(或者g和exec)三者搭配时才有作用。它是pattern的一个属性,一个整数,标示开始下一次匹配的字符位置。

    实例如下:

     var s = 'you love me and I love you';
     var pattern = /you/g;
     var ans;
     do {
       ans = pattern.exec(s);
       console.log(ans);
       console.log(pattern.lastIndex);
     }
     while (ans !== null)  
    
  3. compile

    主要功能:改变当前匹配模式(pattern)

    它是改变匹配模式时用的,用处不大,略过。详见JavaScript compile() 方法

二.String 四大护法

四大护法有些和RegExp三大方法类似,有的更胜一筹。即str.search/match/replace/split形式。

既然是String的方法,当然参数可以只用字符串而不用pattern。

  1. search

    主要功能:搜索指定字符串中是否含有某子串(或者匹配模式),如有,返回子串在原串中的初始位置,如没有,返回-1。

    是不是和test类似呢?test只能判断有木有,search还能返回位置!当然test()如果有需要能继续找下去,而search则会自动忽略g(如果有的话)。实例如下:

     var s = 'you love me and I love you';
     var pattern = /you/;
     var ans = s.search(pattern);
     console.log(ans);  // 0
    

    话说和String的indexOf方法有点相似,不同的是indexOf方法可以从指定位置开始查找,但是不支持正则。

  2. match

    主要功能:和exec类似,从指定字符串中查找子串或者匹配模式,找到返回数组,没找到返回null

    match是exec的轻量版,当不使用全局模式匹配时,match和exec返回结果一致;当使用全局模式匹配时,match直接返回一个字符串数组,获得的信息远没有exec多,但是使用方式简单。实例如下:

     var s = 'you love me and I love you';
     console.log(s.match(/you/));    // ["you", index: 0, input: "you love me and I love you"]
     console.log(s.match(/you/g));   // ["you", "you"]
    
  3. replace

    主要功能:用另一个子串替换指定字符串中的某子串(或者匹配模式),返回替换后的新的字符串 str.replace(‘搜索模式’,’替换的内容’) 如果用的是pattern并且带g,则全部替换;否则替换第一处。实例如下:

     var s = 'you love me and I love you';
     console.log(s.replace('you', 'zichi')); // zichi love me and I love you
     console.log(s.replace(/you/, 'zichi')); // zichi love me and I love you
     console.log(s.replace(/you/g, 'zichi'));    // zichi love me and I love zichi
    

    如果需要替代的内容不是指定的字符串,而是跟匹配模式或者原字符串有关,那么就要用到$了(记住这些和$符号有关的东东只和replace有关哦)。

怎么用?看个例子就明白了。

    var s = 'I love you';
    var pattern = /love/;
    var ans = s.replace(pattern, '$`' + '$&' + "$'");
    console.log(ans); // I I love you you
没错,’$`’ + ‘$&’ + “$’”其实就相当于原串了!

**replace的第二个参数还能是函数**,看具体例子前先看一段介绍:

注意:第一个参数是匹配到的子串,接下去是子表达式匹配的值,如果要用子表达式参数,则必须要有第一个参数(表示匹配到的串),也就是说,如果要用第n个参数代表的值,则左边参数都必须写出来。最后两个参数跟exec后返回的数组的两个属性差不多。

    var s = 'I love you';
    var pattern = /love/;
    var ans = s.replace(pattern, function(a) {  // 只有一个参数,默认为匹配到的串(如还有参数,则按序表示子表达式和其他两个参数)
      return a.toUpperCase();
    });
    console.log(ans); // I LOVE you
  1. split

    主要功能:分割字符串

    字符串分割成字符串数组的方法(另有数组变成字符串的join方法)。直接看以下例子:

     var s = 'you love me and I love you';
     var pattern = 'and';
     var ans = s.split(pattern);
     console.log(ans);   // ["you love me ", " I love you"]
    

    如果你嫌得到的数组会过于庞大,也可以自己定义数组大小,加个参数即可:

     var s = 'you love me and I love you';
     var pattern = /and/;
     var ans = s.split(pattern, 1);
     console.log(ans);   // ["you love me "]
    
  2. RegExp 字符

- \s 任意空白字符 \S相反 空白字符可以是: 空格符 (space character) 制表符 (tab character) 回车符 (carriage return character) 换行符 (new line character) 垂直换行符 (vertical tab character) 换页符 (form feed character)       
- \b是正则表达式规定的一个特殊代码,代表着单词的开头或结尾,也就是单词的分界处。虽然通常英文的单词是由空格,标点符号或者换行来分隔的,但是\b并不匹配这些单词分隔字符中的任何一个,它只匹配一个位置。(和^ $ 以及零宽断言类似)
- \w 匹配字母或数字或下划线   [a-z0-9A-Z_]完全等同于\w

三. 贪婪匹配和懒惰匹配

  1. 什么是贪婪匹配?贪婪匹配就是在正则表达式的匹配过程中,默认会使得匹配长度越大越好。

     var s = 'hello world welcome to my world';
     var pattern = /hello.*world/;
     var ans = pattern.exec(s);
     console.log(ans)  // ["hello world welcome to my world", index: 0, input: "hello world welcome to my world"]
    

    以上例子不会匹配最前面的Hello World,而是一直贪心的往后匹配。

    那么我需要最短的匹配怎么办?很简单,加个‘?’即可,这就是传说中的懒惰匹配,即匹配到了,就不往后找了。

     var s = 'hello world welcome to my world';
     var pattern = /hello.*?world/;
     var ans = pattern.exec(s);
     console.log(ans)  // ["hello world", index: 0, input: "hello world welcome to my world"]
    

    懒惰限定符(?)添加的场景如下:

    子表达式

    表示方式
    用一个小括号指定:

     var s = 'hello world';
     var pattern = /(hello)/;
     var ans = pattern.exec(s);
     console.log(ans);
    

    子表达式出现场景
    在exec中数组输出子表达式所匹配的值:

         var s = 'hello world';
         var pattern = /(h(e)llo)/;
         var ans = pattern.exec(s);
         console.log(ans); // ["hello", "hello", "e", index: 0, input: "hello world"]
    

    在replace中作为替换值引用:

     var s = 'hello world';
     var pattern = /(h\w*o)\s*(w\w*d)/;
     var ans = s.replace(pattern, '$2 $1')
     console.log(ans); // world hello
    

    四.后向引用 & 零宽断言

  2. 子表达式的序号问题

    简单地说:从左向右,以分组的左括号为标志,第一个出现的分组的组号为1,第二个为2,以此类推。

    复杂地说:分组0对应整个正则表达式实际上组号分配过程是要从左向右扫描两遍的:第一遍只给未命名组分配,第二遍只给命名组分配--因此所有命名组的组号都大于未命名的组号。可以使用(?:exp)这样的语法来剥夺一个分组对组号分配的参与权.

  3. 后向引用

    如果我们要找连续两个一样的字符,比如要找两个连续的c,可以这样/c{2}/,如果要找两个连续的单词hello,可以这样/(hello){2}/,但是要在一个字符串中找连续两个相同的任意单词呢,比如一个字符串hellohellochinaworldworld,我要找的是hello和world,怎么找?

    这时候就要用后向引用。看具体例子:

     var s = 'hellohellochinaworldworld';
     var pattern = /(\w+)\1/g;
     var a = s.match(pattern);
     console.log(a); // ["hellohello", "worldworld"]
    

    这里的\1就表示和匹配模式中的第一个子表达式(分组)一样的内容,\2表示和第二个子表达式(如果有的话)一样的内容,\3 \4 以此类推。(也可以自己命名,详见参考文献)

    或许你觉得数组里两个hello两个world太多了,我只要一个就够了,就又要用到子表达式了。因为match方法里是不能引用子表达式的值的,我们回顾下哪些方法是可以的?没错,exec和replace是可以的!

    exec方式:

    var s = ‘hellohellochinaworldworld’;
    var pattern = /(\w+)\1/g;
    var ans;
    do {

     ans = pattern.exec(s);
     console.log(ans);
    

    } while(ans !== null);

    // result
    // [“hellohello”, “hello”, index: 0, input: “hellohellochinaworldworld”] index.html:69
    // [“worldworld”, “world”, index: 15, input: “hellohellochinaworldworld”] index.html:69
    // null
    如果输出只要hello和world,console.log(ans[1])即可。

    replace方式:

         var s = 'hellohellochinaworldworld';
         var pattern = /(\w+)\1/g;
         var ans = [];
         s.replace(pattern, function(a, b) {
          ans.push(b);
         });
         console.log(ans);   // ["hello", "world"]
    

    如果要找连续n个相同的串,比如说要找出一个字符串中出现最多的字符:

         String.prototype.getMost = function() {
           var a = this.split('');
           a.sort();
           var s = a.join('');
           var pattern = /(\w)\1*/g;
           var a = s.match(pattern);
           a.sort(function(a, b) {
             return a.length < b.length;
           });
           var letter = a[0][0];
           var num = a[0].length;
           return letter + ': ' + num;
         }
         
         var s = 'aaabbbcccaaabbbcccccc';
         console.log(s.getMost()); // c: 9
    

    如果需要引用某个子表达式(分组),请认准后向引用!

  4. 零宽断言

    它们用于查找在某些内容(但并不包括这些内容)之后的东西,也就是说它们像\b,^,$那样用于指定一个位置,这个位置应该满足一定的条件(即断言)

    (?=exp)
    零宽度正预测先行断言,它断言自身出现的位置的后面能匹配表达式exp。

     // 获取字符串中以ing结尾的单词的前半部分
     var s = 'I love dancing but he likes singing';
     var pattern = /\b\w+(?=ing\b)/g;
     var ans = s.match(pattern);
     console.log(ans); // ["danc", "sing"]
     (?!exp)
    

    零宽度负预测先行断言,断言此位置的后面不能匹配表达式exp

     // 获取第五位不是i的单词的前四位
     var s = 'I love dancing but he likes singing';
     var pattern = /\b\w{4}(?!i)/g;
     var ans = s.match(pattern);
     console.log(ans); // ["love", "like"]
     javascript正则只支持前瞻,不支持后瞻((?<=exp)和(?<!exp))。
    

    关于零宽断言的具体应用可以参考综合应用一节给字符串加千分符。

五.字符转义

  1. 因为某些字符已经被正则表达式用掉了,比如. * ( ) / \ [],所以需要使用它们(作为字符)时,需要用\转义

     var s = 'http://www.cnblogs.com/zichi/';
     var pattern = /http:\/\/www\.cnblogs\.com\/zichi\//;
     var ans = pattern.exec(s);
     console.log(ans); // ["http://www.cnblogs.com/zichi/", index: 0, input: "http://www.cnblogs.com/zichi/"]
    
  2. 分支条件

    如果需要匹配abc里的任意字母,可以用[abc],但是如果不是单个字母那么简单,就要用到分支条件。

    分支条件很简单,就是用|表示符合其中任意一种规则。

     var s = "I don't like you but I love you";
     var pattern = /I.*(like|love).*you/g;
     var ans = s.match(pattern);
     console.log(ans); // ["I don't like you but I love you"]
    

    答案执行了贪婪匹配,如果需要懒惰匹配,则:

     var s = "I don't like you but I love you";
     var pattern = /I.*?(like|love).*?you/g;
     var ans = s.match(pattern);
     console.log(ans); // ["I don't like you", "I love you"]
    
  3. 综合应用

    去除字符串首尾空格(replace)

     String.prototype.trim = function() {
       return this.replace(/(^\s*)|(\s*$)/g, "");
     };
     var s = '    hello  world     ';
     var ans = s.trim();
     console.log(ans.length);    // 12
    

    给字符串加千分符(零宽断言)

     String.prototype.getAns = function() {
       var pattern = /(?=((?!\b)\d{3})+$)/g;
       return this.replace(pattern, ',');
     }
     
     var s = '123456789';
     console.log(s.getAns());  // 123,456,789
    

    找出字符串中出现最多的字符(后向引用)

     String.prototype.getMost = function() {
       var a = this.split('');
       a.sort();
       var s = a.join('');
       var pattern = /(\w)\1*/g;
       var a = s.match(pattern);
       a.sort(function(a, b) {
         return a.length < b.length;
       });
       var letter = a[0][0];
       var num = a[0].length;
       return letter + ': ' + num;
     }
     
     var s = 'aaabbbcccaaabbbcccccc';
     console.log(s.getMost()); // c: 9
    

    常用匹配模式(持续更新)
    只能输入汉字:

     /^[\u4e00-\u9fa5]{0,}$/
    

六.总结

  1. test:检查指定字符串中有没有某子串(或某匹配模式),返回true或者false;如有必要可以进行全局模式搜索。

  2. exec:检查指定字符串中有没有某子串(或者匹配模式),如有返回数组(数组信息丰富,可参考上文介绍),如没有返回null;如有必要可以进行全局搜索找出所有子串(或者匹配模式)的信息,信息中含有匹配模式中子表达式所对应的字符串。

  3. compile:修改正则表达式中的pattern

  4. search:检查指定字符串中有没有某子串(或者匹配模式),如有返回子串(或者匹配模式)在原串中的开始位置,如没有返回-1。不能进行全局搜索。

  5. match:检查指定字符串中有没有某子串(或者匹配模式),非全局模式下返回信息和exec一致;如进行全局搜索,直接返回字符串数组。(如不需要关于每个匹配的更多信息,推荐用match而不是exec)

  6. replace:检查指定字符串中有没有某子串(或者匹配模式),并用另一个子串代替(该子串可以跟原字符串或者搜索到的子串有关);如启动g,则全局替换,否则只替换第一个。replace方法可以引用子表达式所对应的值。

  7. split:用特定模式分割字符串,返回一个字符串数组;与Array的join方法正好相反。

  8. 子表达式:用括号括起来的正则匹配表达式,用后向引用可以对其进行引用;也可以和exec或者replace搭配获取其真实匹配值。

  9. 后向引用 :对子表达式所在分组进行引用。

  10. 零宽断言:和\b ^ 以及$类似的某个位置概念。

一. AJAX简介

  1. AJAX = 异步 JavaScript 和 XML。
  2. AJAX 是一种用于创建快速动态网页的技术。
  3. 通过在后台与服务器进行少量数据交换,AJAX 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。
  4. 传统的网页(不使用 AJAX)如果需要更新内容,必需重载整个网页面。
  5. 有很多使用 AJAX 的应用程序案例:新浪微博、Google 地图、开心网等等。

二. 创建 XMLHttpRequest 对象

  1. XMLHttpRequest 是 AJAX 的基础。
  2. 所有现代浏览器均支持 XMLHttpRequest 对象(IE5 和 IE6 使用 ActiveXObject)。
  3. XMLHttpRequest 用于在后台与服务器交换数据。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。
  4. 所有现代浏览器(IE7+、Firefox、Chrome、Safari 以及 Opera)均内建 XMLHttpRequest 对象。
  1. 创建 XMLHttpRequest 对象的语法:

     variable=new XMLHttpRequest();
    

    老版本的 Internet Explorer (IE5 和 IE6)使用 ActiveX 对象:

     variable=new ActiveXObject("Microsoft.XMLHTTP");
    
  2. 为了应对所有的现代浏览器,包括 IE5 和 IE6,请检查浏览器是否支持 XMLHttpRequest 对象。如果支持,则创建 XMLHttpRequest 对象。如果不支持,则创建 ActiveXObject :

     var xmlhttp;
     if (window.XMLHttpRequest)
     {
         //  IE7+, Firefox, Chrome, Opera, Safari 浏览器执行代码
         xmlhttp=new XMLHttpRequest();
     }
     else
     {
         // IE6, IE5 浏览器执行代码
         xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
     }
    

    三.请求

  3. 向服务器发送请求请求:

    • XMLHttpRequest 对象用于和服务器交换数据。

    • 如需将请求发送到服务器,我们使用 XMLHttpRequest 对象的 open() 和 send() 方法:

      xmlhttp.open(“GET”,”ajax_info.txt”,true);
      xmlhttp.send();

  4. open(method,url,async):规定请求的类型、URL 以及是否异步处理请求。

    • method:请求的类型;GET 或 POST
    • url:文件在服务器上的位置
    • async:true(异步)或 false(同步)
  5. send(string):将请求发送到服务器。

    • string:仅用于 POST 请求
  6. GET 请求

    一个简单的 GET 请求:

     xmlhttp.open("GET","/try/ajax/demo_get.php",true);
     xmlhttp.send();
    
  7. POST 请求

    一个简单 POST 请求:

     xmlhttp.open("POST","/try/ajax/demo_post.php",true);
     xmlhttp.send();
    
  1. GET 还是 POST?
    与 POST 相比,GET 更简单也更快,并且在大部分情况下都能用。
    然而,在以下情况中,请使用 POST 请求:
    • 无法使用缓存文件(更新服务器上的文件或数据库)
    • 向服务器发送大量数据(POST 没有数据量限制)
    • 发送包含未知字符的用户输入时,POST 比 GET 更稳定也更可靠

四.响应

  1. 服务器响应

    如需获得来自服务器的响应,请使用 XMLHttpRequest 对象的responseText 或 responseXML 属性。

    • responseText:获得字符串形式的响应数据。
    • responseXML:获得 XML 形式的响应数据。
  2. responseText 属性

    如果来自服务器的响应并非 XML,请使用 responseText 属性。

    • responseText 属性返回字符串形式的响应,因此您可以这样使用:

         document.getElementById("myDiv").innerHTML=xmlhttp.responseText;
      
  3. responseXML 属性

    如果来自服务器的响应是 XML,而且需要作为 XML 对象进行解析,请使用 responseXML 属性:

     请求 cd_catalog.xml 文件,并解析响应:
     xmlDoc=xmlhttp.responseXML;
     txt="";
     x=xmlDoc.getElementsByTagName("ARTIST");
     for (i=0;i<x.length;i++)
     {
         txt=txt + x[i].childNodes[0].nodeValue + "<br>";
     }
     document.getElementById("myDiv").innerHTML=txt;
    

    五.onreadystatechange 事件

  4. 当请求被发送到服务器时,我们需要执行一些基于响应的任务。

  5. 每当 readyState 改变时,就会触发 onreadystatechange 事件。

  6. readyState 属性存有 XMLHttpRequest 的状态信息。

  7. 下面是 XMLHttpRequest 对象的三个重要的属性:

    • onreadystatechange: 存储函数(或函数名),每当 readyState 属性改变时,就会调用该函数。

    • readyState:存有 XMLHttpRequest 的状态。从 0 到 4 发生变化。0: 请求未初始化1: 服务器连接已建立2: 请求已接收3: 请求处理中4: 请求已完成,且响应已就绪.

    • status: 200: “OK” 404: 未找到页面

      在 onreadystatechange 事件中,我们规定当服务器响应已做好被处理的准备时所执行的任务。

      当 readyState 等于 4 且状态为 200 时,表示响应已就绪:

      xmlhttp.onreadystatechange=function()

       {
       if (xmlhttp.readyState==4 && xmlhttp.status==200)
         {
         document.getElementById("myDiv").innerHTML=xmlhttp.responseText;
         }
       }
      

      注意: onreadystatechange 事件被触发 5 次(0 - 4),对应着 readyState 的每个变化。

每一个node.js执行文件,都自动创建一个module对象,同时,module对象会创建一个叫exports的属性,初始化的值是 {}

 module.exports = {};

Node.js为了方便地导出功能函数,node.js会自动地实现以下这个语句

//foo.js

     exports.a = function(){
         console.log('a')
     }
     exports.a = 1 


//test.js

     var x = require('./foo');
     console.log(x.a)

result:

1    

这里,exports是引用 module.exports的值。

module.exports 被改变的时候,exports不会被改变,而模块导出的时候,真正导出的执行是module.exports,而不是exports
再看看下面例子

//foo.js

     exports.a = function(){
        console.log('a')
     }
 module.exports = {a: 2}
 exports.a = 1 


//test.js

 var x = require('./foo');
 console.log(x.a)

result:2

exports在module.exports 被改变后,失效。

是不是开始有点廓然开朗,下面将会列出开源模块中,经常看到的几个使用方式。

1. module.exports = View

function View(name, options) { 
   options = options || {};
   this.name = name;
   this.root = options.root;
   var engines = options.engines;
   this.defaultEngine = options.defaultEngine;
   var ext = this.ext = extname(name);
   if (!ext && !this.defaultEngine) throw new Error('No default engine was specified and no         extension was provided.');
   if (!ext) name += (ext = this.ext = ('.' != this.defaultEngine[0] ? '.' : '') +     this.defaultEngine);
   this.engine = engines[ext] || (engines[ext] = require(ext.slice(1)).__express);
   this.path = this.lookup(name);
 }

 module.exports = View;

javascript里面有一句话,函数即对象,View 是对象,module.export =View, 即相当于导出整个view对象。外面模块调用它的时候,能够调用View的所有方法。不过需要注意,只有是View的静态方法的时候,才能够被调用,prototype创建的方法,则属于View的私有方法。

//foo.js

     function View(){

     }

     View.prototype.test = function(){
      console.log('test')
     }

     View.test1 = function(){
          console.log('test1')
     }
     module.exports = View


//test.js

     var x = require('./foo');

     console.log(x) //{ [Function: View] test1: [Function] }
     console.log(x.test) //undefined
     console.log(x.test1) //[Function]
     x.test1() //test1

2. var app = exports = module.exports = {};

其实,当我们了解到原理后,不难明白这样的写法有点冗余,其实是为了保证,模块的初始化环境是干净的。同时也方便我们,即使改变了 module.exports 指向的对象后,依然能沿用 exports的特性

 exports = module.exports = createApplication;

 /**
  * Expose mime.
  */

 exports.mime = connect.mime;

例子当中module.exports = createApplication改变了module.exports了,让exports失效,通过exports = module.exports的方法,让其恢复原来的特点。

3. exports.init= function(){}

这种最简单,直接就是导出模块 init的方法。

4. var mongoose = module.exports = exports = new Mongoose;

集多功能一身。

一.方法处理器

可以用 v-on 指令监听 DOM 事件:

//.html
<div id="example">
  <button v-on:click="greet">Greet</button>
</div>

我们绑定了一个单击事件处理器到一个方法 greet。下面在 Vue 实例中定义这个方法:

var vm = new Vue({
  el: '#example',
  data: {
    name: 'Vue.js'
  },
  //在methods对象中定义方法
  methods: {
    greet: function (event) {
      //方法内this指向vm
      alert('Hello ' + this.name + '!')
      //event是原DOM事件
      alert(event.target.tagName)
    }
  }
})

// 也可以在 JavaScript 代码中调用方法
vm.greet() // -> 'Hello Vue.js!'

二.内联语句处理器

除了直接绑定到一个方法,也可以用内联 JavaScript 语句:

//.html
<div id="example-2">
  <button v-on:click="say('hi')">Say Hi</button>
  <button v-on:click="say('what')">Say What</button>
</div>


//.js
var vm = new Vue({
  el: '#example-2',
  methods: {
    say: function (msg) {
      alert(msg)
    }
  }
}) 

result:

Hi

What

三.事件修饰符

在事件处理器中经常需要调用 event.preventDefault() 或 event.stopPropagation()。尽管我们在方法内可以轻松做到,不过让方法是纯粹的数据逻辑而不处理 DOM 事件细节会更好。

为了解决这个问题,Vue.js 为 v-on 提供两个 事件修饰符:.prevent 与 .stop。

<!-- 阻止单击事件冒泡 -->
<a v-on:click.stop="doThis"></a>

<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>

<!-- 修饰符可以串联 -->
<a v-on:click.stop.prevent="doThat">

<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>

四.按键修饰符

在监听键盘事件时,我们经常需要检测 keyCode。Vue.js 允许为 v-on 添加按键修饰符:

<!-- 只有在 keyCode 是 13 时调用 vm.submit() -->
<input v-on:keyup.13="submit">

记住所有的 keyCode 比较困难,Vue.js 为最常用的按键提供别名:

<!-- 同上 -->
<input v-on:keyup.enter="submit">

<!-- 缩写语法 -->
<input @keyup.enter="submit">

五.为什么在 HTML 中监听事件?

你可能注意到这种事件监听的方式违背了传统理念 “separation of concern”。不必担心,因为所有的 Vue.js 事件处理方法和表达式都严格绑定在当前视图的 ViewModel 上,它不会导致任何维护困难。实际上,使用 v-on 有几个好处:

  1. 扫一眼 HTML 模板便能轻松定位在 JavaScript 代码里对应的方法。

  2. 因为你无须在 JavaScript 里手动绑定事件,你的 ViewModel 代码可以是非常纯粹的逻辑,和 DOM 完全解耦,更易于测试。

  3. 当一个 ViewModel 被销毁时,所有的事件处理器都会自动被删除。你无须担心如何自己清理它们。

基础用法

可以用 v-model 指令在表单控件元素上创建双向数据绑定。根据控件类型它自动选取正确的方法更新元素。尽管有点神奇,v-model 不过是语法糖,在用户输入事件中更新数据,以及特别处理一些极端例子。

1.text

<span>Message is: {{ message }}</span>
<br>
<input type="text" v-model="message" placeholder="edit me">

result:

Message is:

2.multiline text

<span>Multiline message is:</span>
<p>{{ message }}</p>
<br>
<textarea v-model="message" placeholder="add multiple lines"></textarea>

result:

Message is:

3.Checkbox

单个勾选框,逻辑值:

<input type="checkbox" id="checkbox" v-model="checked">
<label for="checkbox">{{ checked }}</label>

false

4.多个勾选框,绑定到同一个数组:

//.html
<input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
<label for="jack">Jack</label>
<input type="checkbox" id="john" value="John" v-model="checkedNames">
<label for="john">John</label>
<input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
<label for="mike">Mike</label>
<br>
<span>Checked names: {{ checkedNames | json }}</span>


//.js
new Vue({
  el: '...',
  data: {
    checkedNames: []
  }
})

5.Radio

<input type="radio" id="one" value="One" v-model="picked">
<label for="one">One</label>
<br>
<input type="radio" id="two" value="Two" v-model="picked">
<label for="two">Two</label>
<br>
<span>Picked: {{ picked }}</span>


Picked: 6.Select 单选:
<select v-model="selected">
  <option selected>A</option>
  <option>B</option>
  <option>C</option>
</select>
<span>Selected: {{ selected }}</span>

Selected:["A"]

7.绑定 value

对于单选按钮,勾选框及选择框选项,v-model 绑定的 value 通常是静态字符串(对于勾选框是逻辑值):

<!-- 当选中时,`picked` 为字符串 "a" -->
<input type="radio" v-model="picked" value="a">

<!-- `toggle` 为 true 或 false -->
<input type="checkbox" v-model="toggle">

<!-- 当选中时,`selected` 为字符串 "abc" -->
<select v-model="selected">
  <option value="abc">ABC</option>
</select>

但是有时我们想绑定 value 到 Vue 实例的一个动态属性上,这时可以用 v-bind 实现,并且这个属性的值可以不是字符串。

一.条件渲染

1.v-if

例子:

//.html
<div id="vm">  
    <h1 v-if="ok">ok</h1>
    <h1 v-else>no</h1>
</div>



//.js
window.onload=function(){
  var vm = new Vue({
    el:'#vm',
    data:{
        ok:true,
        no:false
        }
    });
}

result:

ok

2.template v-if

因为 v-if 是一个指令,需要将它添加到一个元素上。但是如果我们想切换多个元素呢?此时我们可以把一个