(一)javascript 简介

       能在静态HTML页面上添加一些动态效果;事实上JavaScript除了语法上有点像Java,其他部分基本上没啥关系。

(二)javascript在html中的应用

      JavaScript代码可以直接嵌在网页的任何地方,不过通常我们都把JavaScript代码放到<head>中:

<head>

<script> alert('Hello, world'); </script> </head>

第二种方法是把JavaScript代码放到一个单独的.js文件,然后在HTML中通过<script src="..."></script>引入这个文件:

<head>
  <script src="/static/js/abc.js"></script> </head>

这样,/static/js/abc.js就会被浏览器执行。

把JavaScript代码放入一个单独的.js文件中更利于维护代码,并且多个页面可以各自引用同一份.js文件。

可以在同一个页面中引入多个.js文件,还可以在页面中多次编写<script> js代码... </script>,浏览器按照顺序依次执行。

 (三)基本语法

1,每个语句以;结束,语句块用{...} 比如语句块:
;
{...} 比如语句块:
if (2 > 1) {
    x = 1;
    y = 2; z = 3; }

     注意花括号{...}内的语句具有缩进,通常是4个空格。缩进不是JavaScript语法要求必须的,但缩进有助于我们理解代码的层次,所以编写代码时要遵守缩进规则。很多文本编辑器具有“自动缩进”的功能,可以帮助整理代码。

2,注释:注释一行用//;注释多行用/* */

(四)数据类型
js中,数据具有属性和方法。例如string具有length属性和match方法。
 变量


 2,注释:注释一行用//;注释多行用/* */

(四)数据类型
js中,数据具有属性和方法。例如string具有length属性和match方法。
 变量

 

 

1.数字
  1.数字

        JavaScript不区分整数和浮点数,统一用Number表示,以下都是合法的Number类型:

123; // 整数123
0.456; // 浮点数0.456 1.2345e3; // 科学计数法表示1.2345x1000,等同于1234.5 -99; // 负数 NaN; // NaN表示Not a Number,当无法计算结果时用NaN表示 Infinity; // Infinity表示无限大,当数值超过了JavaScript的Number所能表示的最大值时,就表示为Infinity

javascript应用程序 javascript的相关应用_字符串

 

          2.字符串

        字符串是以单引号'或双引号"括起来的任意文本,比如'abc'"xyz"等等。请注意,''""本身只是一种表示方式,不是字符串的一部分,因此,字符串'abc'只有abc这3个字符。

        1.普通字符串:如果'本身也是一个字符,那就可以用""括起来,比如"I'm OK"包含的字符是I'm,空格,OK这6个字符。如果字符串内部既包含'又包含"怎么办?可以用转义字符\来标识,比       如:'I\'m \"OK\"!'(即:在一个字符串中,每个'或者"前面加一个\即可,例如'm前面加了\,\"OK\"中,前面的"和后面的"的前面都加了\);表示的字符串内容是:I'm "OK"!

       2.多行字符串

      由于多行字符串用\n写起来比较费事,所以最新的ES6标准新增了一种多行字符串的表示方法,用` ... `表示:

`这是一个
多行
字符串`;

        3.多个字符串串连成一个字符串:要把多个字符串连接起来,可以用+号连接:

var name = '小明';
var age = 20;
var message = '你好, ' + name + ', 你今年' + age + '岁了!'; alert(message);

       如果有很多变量需要连接,用+号就比较麻烦。ES6新增了一种模板字符串,表示方法和上面的多行字符串一样,但是它会自动替换字符串中的变量:



var name = '小明'; var age = 20; var message = `你好, ${name}, 你今年${age}岁了!`; alert(message);



javascript应用程序 javascript的相关应用_ViewUI_02

javascript应用程序 javascript的相关应用_ViewUI_03





         4.操作字符串(对于字符串的操作是改变不了字符串的,即字符串是不可以改变的,均返回新的字符串

            <1>获取字符串的长度:var s="hello, world!";

                                            var Length=s.length;   //通过函数length返回s的长度值:13,将该返回值符给Length

            <2>获取字符串中的某个指定位置的字符,则用数组下标的方式获取:var s="hello, world!";

s[0]; // 'H'

6]; // ' '

7]; // 'w'12]; // '!'

13]; // undefined 超出范围的索引不会报错,但一律返回undefined

          要特别注意:若在上例中,我们将s[0]=“w”;那么s字符串的值仍然为"hello, world!"

        <3>JavaScript为字符串提供了一些常用方法,注意,调用这些方法本身不会改变原有字符串的内容,而是返回一个新字符串:

                 toUpperCase:将字符串全部变为大写字符串;

'Hello';

//s.toUpperCase()返回'HELLO',将该返回值符给变量t;

               toLowerCase:把一个字符串全部变为小写: 

var s = 'Hello';
                           var lower = s.toLowerCase(); // 返回'hello'并赋值给变量lower

 


indexOf() 方法

:indexOf()会搜索指定字符串出现的位置: 

var s = 'hello, world';
                          var t = s.indexOf('world'); // 返回7,t的值为7:即word字符串中的首字母w存在s[7]中 ,固返回数字7
                          var w = s.indexOf('World'); // w的值为-1:字符串没有找到指定的子串,返回-1

lastIndexOf() 方法

 

substring:substring()返回指定索引区间的子串:

var s = 'hello, world'
                        s.substring(0, 5); // 从索引0开始到5(不包括5),返回'hello' s.substring(7); // 从索引7开始到结束,返回'world'

charAt() 方法

定义和用法

charAt() 方法可返回指定位置的字符。

请注意,JavaScript 并没有一种有别于字符串类型的字符数据类型,所以返回的字符是长度为 1 的字符串。

javascript应用程序 javascript的相关应用_javascript应用程序_04

javascript应用程序 javascript的相关应用_java_05

concat() 方法

定义和用法

concat() 方法用于连接两个或多个字符串。

javascript应用程序 javascript的相关应用_javascript_06

javascript应用程序 javascript的相关应用_字符串_07

 

match() 方法

javascript应用程序 javascript的相关应用_字符串_08

匹配指定的字符串

javascript应用程序 javascript的相关应用_ViewUI_09

 

正则表达式的匹配

javascript应用程序 javascript的相关应用_字符串_10

javascript应用程序 javascript的相关应用_字符串_11

replace() 方法

定义和用法

javascript应用程序 javascript的相关应用_java_12

split() 方法

定义和用法

split() 方法用于把一个字符串分割成字符串数组。

javascript应用程序 javascript的相关应用_java_13

javascript应用程序 javascript的相关应用_java_14

javascript应用程序 javascript的相关应用_java_15

javascript应用程序 javascript的相关应用_javascript_16

         这里第一句话,有空格来分,结果在输出中,空格变成了,字符串数组中不存在空格。

以什么来分,什么就不在字符数组中显示。例如,以a来分

javascript应用程序 javascript的相关应用_ViewUI_17

也就是说,以什么来分,什么就变为字符串中的逗号,即字符串数组中没有该字符串。

substr() 方法

定义和用法

substr() 方法可在字符串中抽取从 start 下标开始的指定数目的字符。

语法


stringObject.substr(start,length)


javascript应用程序 javascript的相关应用_javascript_18

javascript应用程序 javascript的相关应用_字符串_19

javascript应用程序 javascript的相关应用_字符串_20

javascript应用程序 javascript的相关应用_javascript应用程序_21

javascript应用程序 javascript的相关应用_字符串_22

 


replace() 方法用于在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串。

         3.布尔值

         一个布尔值只有truefalse两种值,要么是true,要么是false,可以直接用truefalse表示布尔值,也可以通过布尔运算计算出来: 两种运算符可以产生布尔值:比较运算符与逻辑           运算符

         逻辑运算符(&&,||,!):&&:a&&b,只有a和b均为true,该表达式结果才为true否则为false;||:a||b,只要a、b中有一个为true,该表达结果就为true否则为 false;                !:!若a为true,则!a结果为false否则相反;

         比较运算符(>、<、==、===、!= ):==与===的区别:

第一种是==比较,它会自动转换数据类型再比较,很多时候,会得到非常诡异的结果;

第二种是===比较,它不会自动转换数据类型,如果数据类型不一致,返回false,如果一致,再比较。

由于JavaScript这个设计缺陷,不要使用==比较,始终坚持使用===比较

另一个例外是NaN这个特殊的Number与所有其他值都不相等,包括它自己:

NaN === NaN; // false

唯一能判断NaN的方法是通过isNaN()函数:

isNaN(NaN); // true

最后要注意浮点数的相等比较:

1 / 3 === (1 - 2 / 3); // false

这不是JavaScript的设计缺陷。浮点数在运算过程中会产生误差,因为计算机无法精确表示无限循环小数。要比较两个浮点数是否相等,只能计算它们之差的绝对值,看是否小于某个阈值:

Math.abs(1 / 3 - (1 - 2 / 3)) < 0.0000001; // true

           4. 数组

定义数组

数组对象用来在单独的变量名中存储一系列的值。

我们使用关键词 new 来创建数组对象。下面的代码定义了一个名为 myArray 的数组对象:


var myArray=new Array()


有两种向数组赋值的方法(你可以添加任意多的值,就像你可以定义你需要的任意多的变量一样)。

javascript应用程序 javascript的相关应用_字符串_23

javascript应用程序 javascript的相关应用_ViewUI_24

javascript应用程序 javascript的相关应用_java_25

       数组是一组按顺序排列的集合,集合的每个值称为元素。JavaScript的数组可以包括任意数据类型。例如:[1, 2, 3.14, 'Hello', null, true];

       上述数组包含6个元素。数组用[]表示,元素之间用,分隔。

另一种创建数组的方法是通过Array()函数实现:new Array(1, 2, 3); // 创建了数组[1, 2, 3]

然而,出于代码的可读性考虑,强烈建议直接使用[]

数组的元素可以通过索引来访问。请注意,索引的起始值为0

var arr = [1, 2, 3.14, 'Hello', null, true]; arr[0]; // 返回索引为0的元素,即1 arr[5]; // 返回索引为5的元素,即true arr[6]; // 索引超出了范围,返回undefined

javascript应用程序 javascript的相关应用_字符串_26

        操作数组   
              <1>  获取数组的长度:数组名.length //返回数组长度如:6    

                               

var arr=[0,'hello',true,null,7,9];
                                    var len=arr.length;    //arr.length将会返回一个值即6,并且将arr.length的值赋给len
                                    document.write(len);

             <2>  修改数组的长度(变长和缩短)

                     变长:      

var arr=[0,'hello',true,null,7,9];
                                    arr.length=7; //将数组arr的长度变为7,此时arr数组为[0,'hello',true,null,7,9,undefined];
                                    var arr=[0,'hello',true,null,7,9];//
                                    arr[6]=6;//这相当于将arr的长度变为7,此时arr数组为[0,'hello',true,null,7,9,6];

                     变短:      

var arr=[0,'hello',true,null,7,9];
                                    arr.length=3;//将数组的长度变为3,此时arr数组为[0,'hello',true]

             <3> 修改数组元素的值(Array可以通过索引把对应的元素修改为新的值,因此,对Array的索引进行赋值会直接修改这个Array

                               

var arr=[0,'hello',true,null,7,9];
                                    arr[0]=1;//此时arr数组为 [1,'hello',true,null,7,9]      
indexOf()
                                    var arr=[0,'hello',true,null,7,9,0];
                                   arr.indexOf(0);//返回值为0的元素的下表值,即0;
                                   arr.indexOf('hello');//返回值为‘hello’的元素的下表值,即1;

indexOf总是返回第一个元素的位置,数组最后一个0,返回的元素位置不是6,而是0;

             <5>  截取Array的部分元素,然后返回一个新的Arrayslice()  用法: 数组名.slice(开始下标值,截止下标值)    不会改变原数组

                             

var arr=[0,'hello',true,null,7,9];
                                   arr.slice(3,5);//从下标值为3开始即从arr[3]开始至arr[5]之前的元素,执行后,返回新的数组[null,7]
                                   arr.slice(3);//截取从arr[3]开始的所有元素,执行后,返回新的数组[null,7,9]
                                   arr.slice();//相当于复制整个数组

             <6>向数组的尾部增加和删除元素:增加:push();删除pop()  push和pop改变原数组

                         增加元素:push()   用法:数组名.push(元素值1,元素值2,元素值3......)  

                                   

var arr=[0,'hello',true,null,7,9];
                                   arr.push(3,11,'helloword','wo');//向数组arr的后面增加元素,然后, arr.push(3,11,'helloword','wo')返回arr现在的长度值,11
                                   arr;//此时的arr为[0,'hello',true,null,7,9,3,11,'helloword','wo']

                       删除元素:pop()    用法:数组名.pop();

                             

var arr=[0,'hello',true,null,7,9];    
                                  arr.pop();//pop()返回被删除的元素即9
                       arr.pop(); arr.pop(); arr.pop(); // 再连续pop 3次
                       arr//此时arr为[0,'hello']

头部增加和删除元素:增加:unshift()和删除:shift()

1, 2];
'A', 'B'); // 返回Array新的长度: 4
// ['A', 'B', 1, 2]
// 'A'
// ['B', 1, 2]
// 连续shift 3次
// []
// 空数组继续shift不会报错,而是返回undefined
  
// []


                     <8>给数组进行默认排序:sort()  用法:数组名.sort();特点:改变arr数组

                               var arr = [8,2,10,6];

                               arr.sort();//将arr重新排序,并将排序后的arr数组返回

                     <9>reverse:把整个Array的元素给掉个个,也就是反转:

var arr = ['one', 'two', 'three'];
                      arr.reverse(); 
  
// ['three', 'two', 'one'] <10>splice


                splice()方法是修改Array的“万能方法”,它可以从指定的索引开始删除若干元素,然后再从该位置添加若干元素:                      

'Microsoft', 'Apple', 'Yahoo', 'AOL', 'Excite', 'Oracle']; 2, 3, 'Google', 'Facebook'); // 返回删除的元素 ['Yahoo', 'AOL', 'Excite'] arr; // ['Microsoft', 'Apple', 'Google', 'Facebook', 'Oracle'] 2, 2); // ['Google', 'Facebook'] arr; // ['Microsoft', 'Apple', 'Oracle'] 2, 0, 'Google', 'Facebook'); // 返回[],因为没有删除任何元素 arr; // ['Microsoft', 'Apple', 'Google', 'Facebook', 'Oracle']

concat()
Array
Array

用数组的元素组成字符串 - join()    和string的slipt相反。

 

文字数组 - sort()

javascript应用程序 javascript的相关应用_javascript_27

isArray() 方法


定义和用法

isArray() 方法用于判断一个对象是否为数组。

如果对象是数组返回 true,否则返回 false。

javascript应用程序 javascript的相关应用_javascript应用程序_28

 

Math(算数)对象

javascript应用程序 javascript的相关应用_javascript_29

javascript应用程序 javascript的相关应用_ViewUI_30

javascript应用程序 javascript的相关应用_java_31

javascript应用程序 javascript的相关应用_java_32

floor():返回小于等于x的最大整数

javascript应用程序 javascript的相关应用_javascript_33

 

reduce() 方法

定义和用法

reduce() 方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。

reduce() 可以作为一个高阶函数,用于函数的 compose。

注意: reduce() 对于空数组是不会执行回调函数的。

javascript应用程序 javascript的相关应用_java_34

 

       

javascript应用程序 javascript的相关应用_ViewUI_35

例:

var numbers=[65,44,12,4];
    numbers.reduce(function(x1,x2){
       return  x1+x2;
})

 在这里,第一步,X1为65,x2为44.第二步:x1为(65+44)   109,x2为12;第三步:x1为(109+12)121,x2为4.第四步:最后return 125(121+4) 

如果上述reduce中除了回调函数还有参数,则第一步X1为 参数值,X2为65                                 


js 数组 map方法


map
这里的map不是“地图”的意思,而是指“映射”。[].map(); 基本用法跟forEach方法类似:

array.map(callback,[ thisObject]);

callback的参数也类似:

[].map(function(value, index, array) {
// ...
});

map方法的作用不难理解,“映射”嘛,也就是原数组被“映射”成对应新数组。下面这个例子是数值项求平方:

var data = [1, 2, 3, 4];
var arrayOfSquares = data.map(function (item) {
  return item * item;
});
alert(arrayOfSquares); // [1, 4, 9, 16]

callback需要有return值,如果没有,就像下面这样:

var data = [1, 2, 3, 4];
  var arrayOfSquares = data.map(function() {});
  arrayOfSquares.forEach(console.log);

结果可以看到,数组所有项都被映射成了undefined

在实际使用的时候,我们可以利用map方法方便获得对象数组中的特定属性值们。例如下面这个例子(之后的兼容demo也是该例子):


var users = [
  {name: "张含韵", "email": "zhang@email.com"},
  {name: "江一燕",   "email": "jiang@email.com"},
  {name: "李小璐",  "email": "li@email.com"}
];

var emails = users.map(function (user) { return user.email; });

console.log(emails.join(", ")); // zhang@email.com, jiang@email.com, li@email.com


Array.prototype扩展可以让IE6-IE8浏览器也支持map方法:

if (typeof Array.prototype.map != "function") {  Array.prototype.map = function (fn, context) {    var arr = [];
    if (typeof fn === "function") {
      for (var k = 0, length = this.length; k < length; k++) {      
         arr.push(fn.call(context, this[k], k, this));
      }
    }
    return arr;
  };

 


5.对象

 

(Object.create(o),如果o是一个字面量对象或实例对象,那么相当于是实现了对象的浅拷贝)

 

JavaScript 中的所有事物都是对象:字符串、数值、数组、函数...

此外,JavaScript 允许自定义对象。

(一)对象的定义:

对象只是带有属性和方法的特殊数据类型。

javascript应用程序 javascript的相关应用_javascript_36

 

javascript应用程序 javascript的相关应用_字符串_37

 

JavaScript的对象是一组由键-值组成的无序集合, 

javascript应用程序 javascript的相关应用_javascript_38

 

除了字符串、数值、数组、函数等对象以外,一般我们可以自己定义对象,

例如

var person = {
    name: 'Bob',
    age: 20,
    tags: ['js', 'web', 'mobile'], city: 'Beijing', hasCar: true, zipcode: null };
(二)对象的键值类型

JavaScript对象的键都是字符串类型,值可以是任意数据类型。上述person对象一共定义了6个键值对,其中每个键又称为对象的属性,例如,personname属性为'Bob'zipcode属性为null

要获取一个对象的属性,我们用对象变量.属性名的方式:

person.name; // 'Bob'
person.zipcode; // null
(三)创建对象
1.直接创建某个对象;(不需要创建实例,该对象就是一个具体的对象)2.利用构造器来创建对象(具体的实例来引用对象)

 2.使用构造器来创建对象

javascript应用程序 javascript的相关应用_javascript应用程序_39


(四)对象中的属性和方法的添加

1.直接添加

            在定义的时候就直接添加

         

var obj={
                              name:"zhangsan",
                             age:18
                         }

  2.通过赋值的方式添加

          obj.name="张三"

 

javascript应用程序 javascript的相关应用_字符串_40

javascript应用程序 javascript的相关应用_字符串_41

 

  3.使用defineProperty方法

  • value:属性的值
  • writable:如果为false,属性的值就不能被重写。
  • get: 一旦目标属性被访问就会调回此方法,并将此方法的运算结果返回用户。
  • set:一旦目标属性被赋值,就会调回此方法。
  • configurable:如果为false,则任何尝试删除目标属性或修改属性以下特性(writable, configurable, enumerable)的行为将被无效化。
  • enumerable:是否能在for...in循环中遍历出来或在Object.keys中列举出来。

TheObject.defineProperty()方法定义了一个新的属性直接在一个对象,或修改现有的属性的对象,并返回该对象。

Object.defineProperty(obj,prop,descriptor)

obj:定义的对象属性。

prop:属性的名称定义或修改。
descriptor:该属性被定义或修改描述符。

可使用 Object.defineProperty 函数来执行以下操作:

    向对象添加新属性。  当对象不具有指定的属性名称时,发生此操作。  

    修改现有属性的特性。  当对象已具有指定的属性名称时,发成此操作。  

描述符对象中会提供属性定义,用于描述数据属性或访问器属性的特性。  描述符对象是 Object.defineProperty 函数的参数。 
添加数据属性

在以下示例中,Object.defineProperty 函数向用户定义的对象添加数据属性

var obj = {};
Object.defineProperty(obj, "newDataProperty", {
    value: 101,         //属性默认值
    writable: true,     //是否可以对属性重新赋值
    enumerable: true,   //是否可以枚举,是否可以通过for...in遍历到,是否可以通过Obejct.key()方法获取属性名称
    configurable: true  //是否可以删除属性,是否可以修改属性的writable、enumerable、configurable属性<pre name="code" class="html">    set:undefined,      //属性被赋值时方法调用
    get:undefined       //属性被读取时方法调用
obj
newDataProperty的值为101.

修改数据属性

若要修改对象的属性特性,请将以下代码添加到前面所示的 addDataProperty 函数。   descriptor 参数只包含 writable 特性。  其他数据属性特性保持不变。 

get和set方法(set是存储对象属性触发的方法,get是读取对象属性触发的方法)
get :获取指定属性的值的时候会触发该方法;
set:给某个属性赋值时会触发该方法;
var obj = {};
Object.defineProperty(obj, "newAccessorProperty", {
    set: function (x) {
        document.write("in property set accessor" + newLine);
        this.newaccpropvalue = x;
    },
    get: function () {
        document.write("in property get accessor" + newLine);
        return this.newaccpropvalue;
    },
    enumerable: true,
    configurable: true
});
 
obj.newAccessorProperty = 30;
document.write("Property value: " + obj.newAccessorProperty + newLine);
 
// Output:
// in property set accessor
// in property get accessor
// Property value: 30

(五)对象的属性和方法的操作

<1>访问属性

            访问属性是通过.操作符完成的,但这要求属性名必须是一个有效的变量名。如果属性名包含特殊字符,就必须用''括起来:

var xiaohong = {
    name: '小红',
    'middle-school': 'No.1 Middle School'
};
   1.通过.操作符完成
 xiaoming.name;
xiaohong.name; // '小红'
 2.通过[]操作完成(注意:[key]里面是字符串。)

.
xiaohong.name; // '小红'


'name']; // '小红'

   xiaohong的属性名middle-school不是一个有效的变量,就需要用''括起来。访问这个属性也无法使用.操作符,必须用['xxx']来访问:

xiaohong['middle-school']; // 'No.1 Middle School' 例:var obj={}; 要将var name="zhangsan"设置为obj的属性;‘lisi’设置为"zhangsan"的值,则:必须用obj[name]="lisi"而不能用obj.name="lisi".<2>增加、删除属性

由于JavaScript的对象是动态类型,你可以自由地给一个对象添加或删除属性:

var xiaoming = {
    name: '小明'
};
xiaoming.age; // undefined
xiaoming.age = 18; // 新增一个age属性 xiaoming.age; // 18 delete xiaoming.age; // 删除age属性 xiaoming.age; // undefined delete xiaoming['name']; // 删除name属性 xiaoming.name; // undefined delete xiaoming.school; // 删除一个不存在的school属性也不会报错
 <3>

要判断一个属性是否是xiaoming自身拥有的,而不是继承得到的,可以用hasOwnProperty()方法:

var xiaoming = {
    name: '小明'
};
xiaoming.hasOwnProperty('name'); // true xiaoming.hasOwnProperty('toString'); // false

遍历对象中的键值(JavaScript for...in 循环)

 

javascript应用程序 javascript的相关应用_javascript应用程序_42

javascript应用程序 javascript的相关应用_javascript应用程序_43

 

prototype 对象

面向对象编程很重要的一个方面,就是对象的继承。A 对象通过继承 B 对象,就能直接拥有 B 对象的所有属性和方法。这对于代码的复用是非常有用的。

大部分面向对象的编程语言,都是通过“类”(class)来实现对象的继承。JavaScript 语言的继承则是通过“原型对象”(prototype)。


原型对象概述

构造函数的缺点


JavaScript 通过构造函数生成新对象,因此构造函数可以视为对象的模板实例对象的属性和方法,可以定义在构造函数内部


function Cat (name, color) { this.name = name; this.color = color; } var cat1 = new Cat('大毛', '白色'); cat1.name // '大毛' cat1.color // '白色'


上面代码中,Cat函数是一个构造函数,函数内部定义了name属性和color属性,所有实例对象(上例是cat1)都会生成这两个属性,即这两个属性会定义在实例对象上面。
 通过构造函数为实例对象定义属性,虽然很方便,但是有一个缺点。同一个构造函数的多个实例之间,无法共享属性,从而造成对系统资源的浪费。

Cat
name
color
cat1


function Cat(name, color) { this.name = name; this.color = color; this.meow = function () { console.log('喵喵'); }; } var cat1 = new Cat('大毛', '白色'); var cat2 = new Cat('二毛', '黑色'); cat1.meow === cat2.meow // false


上面代码中,cat1cat2是同一个构造函数的两个实例,它们都具有meow方法。由于meow方法是生成在每个实例对象上面,所以两个实例就生成了两次。也就是说,每新建一个实例,就会新建一个meow方法。这既没有必要,又浪费系统资源,因为所有meow方法都是同样的行为,完全应该共享。


这个问题的解决方法,就是 JavaScript 的原型对象(prototype)。

prototype 属性的作用

JavaScript 继承机制的设计思想就是,原型对象的所有属性和方法,都能被实例对象共享。也就是说,如果属性和方法定义在原型上,那么所有实例对象就能共享,不仅节省了内存,还体现了实例对象之间的联系。

下面,先看怎么为对象指定原型。JavaScript 规定,每个函数都有一个prototype属性,指向一个对象。


function f() {}
typeof f.prototype // "object"


上面代码中,函数f默认具有prototype属性,指向一个对象。

对于普通函数来说,该属性基本无用。但是,对于构造函数来说,生成实例的时候,该属性会自动成为实例对象的原型


function Animal(name) { this.name = name; } Animal.prototype.color = 'white'; var cat1 = new Animal('大毛'); var cat2 = new Animal('二毛'); cat1.color // 'white' cat2.color // 'white'


上面代码中,构造函数Animalprototype属性,就是实例对象cat1cat2的原型对象。原型对象上添加一个color属性,结果,实例对象都共享了该属性。

原型对象的属性不是实例对象自身的属性。只要修改原型对象,变动就立刻会体现在所有实例对象上。


Animal.prototype.color = 'yellow'; cat1.color // "yellow" cat2.color // "yellow" 
color
yellow
color
color
color

如果实例对象自身就有某个属性或方法,它就不会再去原型对象寻找这个属性或方法。


cat1.color = 'black'; cat1.color // 'black' cat2.color // 'yellow' Animal.prototype.color // 'yellow';

上面代码中,实例对象cat1color属性改为black,就使得它不再去原型对象读取color属性,后者的值依然为yellow

总结一下,原型对象的作用,就是定义所有实例对象共享的属性和方法。这也是它被称为原型对象的原因,而实例对象可以视作从原型对象衍生出来的子对象。


Animal.prototype.walk = function () { console.log(this.name + ' is walking'); };


上面代码中,Animal.prototype对象上面定义了一个walk方法,这个方法将可以在所有Animal实例对象上面调用。

原型链

JavaScript 规定,所有对象都有自己的原型对象(prototype)。一方面,任何一个对象,都可以充当其他对象的原型;另一方面,由于原型对象也是对象,所以它也有自己的原型。因此,就会形成一个“原型链”(prototype chain):对象到原型,再到原型的原型……

如果一层层地上溯,所有对象的原型最终都可以上溯到Object.prototype,即Object构造函数的prototype属性。也就是说,所有对象都继承了Object.prototype的属性。这就是所有对象都有valueOftoString方法的原因,因为这是从Object.prototype继承的。

那么,Object.prototype对象有没有它的原型呢?回答是Object.prototype的原型是nullnull没有任何属性和方法,也没有自己的原型。因此,原型链的尽头就是null

读取对象的某个属性时,JavaScript 引擎先寻找对象本身的属性,如果找不到,就到它的原型去找,如果还是找不到,就到原型的原型去找。如果直到最顶层的Object.prototype还是找不到,则返回undefined。如果对象自身和它的原型,都定义了一个同名属性,那么优先读取对象自身的属性,这叫做“覆盖”(overriding)。

注意,一级级向上,在整个原型链上寻找某个属性,对性能是有影响的。所寻找的属性在越上层的原型对象,对性能的影响越大。如果寻找某个不存在的属性,将会遍历整个原型链。

举例来说,如果让构造函数的prototype属性指向一个数组,就意味着实例对象可以调用数组方法。


(五)变量

        变量的命名规则:大小写英文、数字、$_的组合,且不能用数字开头。变量名也不能是JavaScript的关键字,如ifwhile等。申明一个变量用var语句,比如:

var a; // 申明了变量a,此时a的值为undefined
      var $b = 1; // 申明了变量$b,同时给$b赋值,此时$b的值为1  var s_007 = '007'; // s_007是一个字符串  var Answer = true; // Answer是一个布尔值true  var t = null; // t的值是null

    可以把任意数据类型赋值给变量,同一个变量可以反复赋值,而且可以是不同类型的变量,但是要注意只能用var申明一次,例如:

var a = 123; // a的值是整数123
     a = 'ABC'; // a变为字符串

strict模式

JavaScript在中,如果一个变量没有通过var申明就被使用,那么该变量就自动被申明为全局变量:

i = 10; // i现在是全局变量

在同一个页面的不同的JavaScript文件中,如果都不用var申明,恰好都使用了变量i,将造成变量i互相影响,产生难以调试的错误结果。

使用var申明的变量则不是全局变量,它的范围被限制在该变量被申明的函数体内(函数的概念将稍后讲解),同名变量在不同的函数体内互不冲突。

为了修补JavaScript这一严重设计缺陷,ECMA在后续规范中推出了strict模式,在strict模式下运行的JavaScript代码,强制通过var申明变量,未使用var申明变量就使用的,将导致运行错误。

启用strict模式的方法是在JavaScript代码的第一行写上:

'use strict';