繼承
一、混入式繼承
for in
使用for in遍歷對象1的屬性,將所有的屬性添加到另外一個對象2上
這時候就可以稱 對象2 繼承自 對象1
二、原型繼承
- 利用原型中的成員可以被和其相關(guān)的對象共享這一特性,可以實(shí)現(xiàn)繼承,這種實(shí)現(xiàn)繼承的方式就是原型繼承:
- 一、利用對象的動態(tài)特性,為原型對象添加成員,不是嚴(yán)格意義上的繼承;如下例中的p對象繼承自原型對象。
function Person(name,age){
this.name = name;
this.age = age;
}
Person.prototype.sayHello = function(){
console.log("我想死你啦");
}
var p = new Person("馮鞏",50);
p.sayHello();
- 二、直接替換原型對象(原型的使用方式):
- 有風(fēng)險,在替換之后,原有的成員都會丟失;
- 替換原型對象的時候,需要手動去指定原型對象的construtor屬性;
function Person(name,age){
this.name = name;
this.age = age;
}
var parent = {
sayHello:function(){
console.log("我想死你啦");
}
}
//讓p繼承自parent,替換原型對象即可
Person.prototype = parent;
var p = new Person("馮鞏",50);
p.sayHello();
- 三、利用混入給原型對象添加成員
function Person(name,age){
this.name = name;
this.age = age;
}
var parent = {
sayHello:function(){
console.log("朋友們還好嗎?");
}
}
//混入式繼承
for(k in parent){
Person.prototype[k] = parent[k];
}
var p = new Person("馮鞏",50);
p.sayHello();
繼承的應(yīng)用
- 對內(nèi)置對象進(jìn)行擴(kuò)展:
var arr = [1,2,3];
Array.prototype.sayHello = function(){
console.log("我是數(shù)組);
}
arr.sayHello();
- 但是,如果直接修改內(nèi)置對象的原型,會影響到整個開發(fā)團(tuán)隊(duì)。
- 如何安全的擴(kuò)展一個內(nèi)置對象:
function MyArray(){
//添加自己的屬性
this.name = "我是一個數(shù)組";
this.sayHello = function(){
console.log("你好,我是數(shù)組");
}
}
var arr = new Array();
//繼承之后(替換原型,我的數(shù)組中就有了原生數(shù)組的所有屬性和方法)
MyArray.prototype = arr;
var myArr = new MyArray();
三、經(jīng)典繼承
var 對象1 = Object.create(對象2);
這個時候,創(chuàng)建出來的對象1繼承自對象2
Object.create方法是ES5中提出來的,存在兼容性問題
如何解決?
1.檢測瀏覽器是否支持Object.create方法,如果不支持,直接手動給Object添加create方法
2.自定義函數(shù),在函數(shù)內(nèi)部判斷瀏覽器是否支持Object.create方法,如果不支持,則手動創(chuàng)建對象返回,否則直接調(diào)用
function create(obj){
if(Object.create){
return Object.create(obj);
}else{
function F(){
}
F.prototype = obj;
return new F();
}
}
原型鏈
什么是原型鏈
每個構(gòu)造函數(shù)都有原型對象,每個對象都有構(gòu)造函數(shù),每個構(gòu)造函數(shù)的原型對象都是對象,那么這個原型對象也會有構(gòu)造函數(shù),那么這個原型對象的構(gòu)造函數(shù)也會有原型對象,這樣就會形成一個鏈?zhǔn)降慕Y(jié)構(gòu),我們稱之為原型鏈。
原型結(jié)構(gòu)的基本形式
function Person(){}
var p = new Person();
p ---> Person.prototype ---> Object.prototype ---> null
- 屬性搜索原則:
- 當(dāng)訪問一個對象的成員的時候,會先在自身查找,如果找到直接使用;
- 如果沒有找到,就去當(dāng)前對象的原型對象中去查找,如果找到就直接使用;
- 如果沒有找到,繼續(xù)在原型對象的原型對象中查找,如果找到了就直接使用;
- 如果還沒有找到,就繼續(xù)向上查找,直到查找到Object.prototype,如果還沒有找到:是屬性的話就返回undefined,是方法的話就報(bào)錯。
原型繼承是什么?
通過修改原型鏈的結(jié)構(gòu),實(shí)現(xiàn)的繼承方式就是原型繼承
對象和原型的成員關(guān)系
function Person(){};
var p = new Person();
- p對象中包含的成員有:Person.prototype中的成員和自身擁有成員;
- Person.prototype中的成員有:Object.prototype的成員和自身的成員
- p對象可以訪問Person.prototype和Object.prototype中的所有成員
Object.prototype的成員
- constructor:指向和該原型相關(guān)的構(gòu)造函數(shù);
- hasOwnProperty方法:判斷對象本身是否擁有某個屬性;
- isPrototypeOf方法:判斷是否是對象的原型對象;
- properIsEnumerable方法:1.判斷屬性是否屬于對象本身,2.判斷屬性是否可以被遍歷;返回的結(jié)果為:1&&2
- toString:將對象轉(zhuǎn)換成字符串; toLocalString轉(zhuǎn)換成字符串的時候應(yīng)用的本地的設(shè)置格式;
- valueOf 方法:在對象參與運(yùn)算的時候,首先調(diào)用valueOf方法獲取對象的值,如果該值無法參與運(yùn)算,將會調(diào)用toString方法;
-
__proto__屬性:指向當(dāng)前對象的原型對象的屬性,可以使用對象.__proto__去訪問對象的原型對象。
Function
3種創(chuàng)建函數(shù)的方式
* 直接聲明函數(shù):function fn(){}
* 函數(shù)表達(dá)式:var fn = function(){}
* new Function:var fn = new Function();
可以用Function來創(chuàng)建函數(shù):
語法:
//創(chuàng)建一個空的函數(shù)
var 函數(shù)名 = new Function();
//創(chuàng)建一個沒有參數(shù)的函數(shù)
var 函數(shù)名 = new Function("函數(shù)體")
//當(dāng)給Fucntion傳多個參數(shù)的時候,最后一個參數(shù)為函數(shù)體,前面的參數(shù)為創(chuàng)建出來的函數(shù)的形參
var 函數(shù)名 = new Function("參數(shù)1","參數(shù)2", "參數(shù)3",..."函數(shù)體")
//Function接收的所有的參數(shù)都是字符串類型的!?。?
- 創(chuàng)建一個沒有參數(shù)的函數(shù):
var fn = new Function("console.log('我可是函數(shù)體喲?。?!')");
fn();
- 創(chuàng)建一個帶有參數(shù)的函數(shù):
var fn = new Function("a","b","console.log(a+b);");
fn(2,3); //5
arguments對象
- arguments對象是函數(shù)內(nèi)部的一個對象,在函數(shù)調(diào)用的時候,系統(tǒng)會默認(rèn)的將所有傳入的實(shí)參存入該對象;
注意:不管有沒有形參,實(shí)參都會被存入該對象。
是一個偽數(shù)組,arguments.length可以用來表示傳入的實(shí)參的個數(shù);
arguments.callee,指向函數(shù)本身。
例. 不管輸入多少個數(shù),總是輸出最大的數(shù):
直接聲明函數(shù):
function max() {
var maxValue = arguments[0];
for(var i=1; i<arguments.length; i++){
if (maxValue < arguments[i]){
maxValue = arguments[i];
}
}
return maxValue;
};
console.log(max(1, 3, 7, 9, 5));
- 函數(shù)表達(dá)式:
var max = function() {
var maxValue = arguments[0];
for(var i=1; i<arguments.length; i++){
if (maxValue < arguments[i]){
maxValue = arguments[i];
}
}
return maxValue;
};
console.log(max(1, 3, 7, 9, 5));
- new Function:
var max = new Function("a","b","var maxValue=arguments[0];" +
"for(i=1;i<arguments.length;i++){" +
"if(maxValue<arguments[i]){" +
"maxValue=arguments[i];" +
"}}" +
"return maxValue;"
);
console.log(max(1,2,3,4,59));
eval
可以將字符串轉(zhuǎn)換成js代碼并執(zhí)行,但是不推薦使用,存在安全性問題。
var str = "var a = 10;";
eval(str);
console.log(a); //10
- 注意:當(dāng)使用eval解析JSON格式字符串的時候,要注意,會將{}解析為代碼塊
- 1.可以在JSON格式字符串前面拼接
"var 變量名 =":
var obj = "{name:'yijiang',age:10};";
eval("var o = "+obj);
console.log(o);
- 2.用
()把JSON格式字符串括起來,eval("("+JSON格式的字符串+")"):
Function和eval的區(qū)別:
- 共同點(diǎn):都可以將字符串轉(zhuǎn)換成js代碼;
- 不同點(diǎn):
- Function創(chuàng)建出來的是函數(shù),并不會直接調(diào)用,只用手動去調(diào)用才會執(zhí)行;
- eval把字符串轉(zhuǎn)成代碼之后,直接就執(zhí)行了。
靜態(tài)成員和實(shí)例成員
靜態(tài)成員
是指構(gòu)造函數(shù)的屬性和方法。通過構(gòu)造函數(shù)去訪問的屬性和方法就是靜態(tài)成員
實(shí)例成員
是指實(shí)例的屬性和方法。通過對象(實(shí)例)去訪問的屬性和方法就是實(shí)例成員
function Person(){
this.age = 28;
this.brother = "yijiang";
}
Person.prototype = {};
//其中age、brother是實(shí)例成員;
//prototype是靜態(tài)成員
//__proto__是實(shí)例成員
通常情況下:
- 把工具方法作為靜態(tài)成員;
- 把跟對象相關(guān)的方法作為實(shí)例成員。
補(bǔ)充:
instanceof關(guān)鍵字:
對象 instanceof 構(gòu)造函數(shù);:判斷該構(gòu)造函數(shù)的原型是否存在于該對象的原型鏈上。Function的原型鏈:
Function也可以被當(dāng)做一個構(gòu)造函數(shù);
通過Function new出來的函數(shù)可以被當(dāng)做是實(shí)例化的對象;
那么Function這個構(gòu)造函數(shù)也有原型對象,F(xiàn)unction的原型對象是一個空的函數(shù);
Function的原型對象的原型對象是Object.prototype。
- Object構(gòu)造函數(shù) 是通過 Function構(gòu)造函數(shù) 實(shí)例化出來的;
- Function構(gòu)造函數(shù) 也是通過 Function構(gòu)造函數(shù) 實(shí)例化出來的(不要強(qiáng)行去理解,知道就好)
面向?qū)ο罂倧?fù)習(xí)
1.什么叫面向?qū)ο?/p>
-
面向?qū)ο笫且环N思想
- 把解決問題的關(guān)注點(diǎn)放到解決問題所需要的一系列對象上
-
面向過程是一種思想
- 把解決問題的關(guān)注點(diǎn)放到解決問題的每一個詳細(xì)步驟上
2.面向?qū)ο蟮娜筇匦?/p>
封裝
繼承
多態(tài)
3.什么是對象
萬物接對象
4.什么是js對象
鍵值對兒的集合(無序)
5.名詞提煉法
一句話中的所有的名詞都可以被當(dāng)做對象
6.如何用js對象模擬現(xiàn)實(shí)生活中的對象
屬性對應(yīng)特征
方法對應(yīng)行為
7.創(chuàng)建對象的方式
使用對象字面量
使用內(nèi)置的構(gòu)造函數(shù)Object
使用簡單工廠函數(shù)(不推薦使用)
自定義構(gòu)造函數(shù)
8.傳統(tǒng)構(gòu)造函數(shù)中存在的問題
如果把方法定義在構(gòu)造函數(shù)中,每創(chuàng)建一個對象,都會新建一個方法,這樣同樣的代碼會在內(nèi)存中存在多分,造成資源浪費(fèi)
9.如何解決問題8
把方法提取出來定義在全局中,在構(gòu)造函數(shù)中引用該函數(shù)
使用原型來解決,原型中的所有成員都可以被所有跟其關(guān)聯(lián)的對象訪問
10.原型是什么
在構(gòu)造函數(shù)創(chuàng)建的時候,系統(tǒng)默認(rèn)的會為這個構(gòu)造函數(shù)創(chuàng)建并關(guān)聯(lián)一個對象,這個對象就是原型對象
這個原型對象默認(rèn)是一個空的對象,該對象中的所有成員可以被所有通過該構(gòu)造函數(shù)實(shí)例化出來的對象訪問
11.原型的作用
該對象中的所有成員可以被所有通過該構(gòu)造函數(shù)實(shí)例化出來的對象訪問
12.原型的使用方式(實(shí)現(xiàn)原型繼承的方式)
1.利用對象的動態(tài)特性給原型對象添加成員
2.直接替換原型對象
3.通過混入的方式給原型對象添加成員
13.原型鏈
每一個構(gòu)造函數(shù)都有原型對象,每一個原型對象都有構(gòu)造函數(shù),這樣就形成一個鏈?zhǔn)降慕Y(jié)構(gòu),稱之為原型鏈
14.繼承的實(shí)現(xiàn)方式
1.混入式繼承 for-in
2.原型繼承 通過更改原型鏈的結(jié)構(gòu),實(shí)現(xiàn)的繼承,就是原型繼承
3.經(jīng)典繼承 Object.creat() 有兼容性問題
//var 對象名 = Object.create(要繼承的對象)
15.Object.prototype的成員
constructor 屬性 指向該原型相關(guān)的構(gòu)造函數(shù)
hasOwnProperty 方法 判斷對象本身是否擁有某個屬性 obj.hasOwnProperty("屬性名")
isPrototypeOf 方法 判斷一個對象是不是另一個對象的原型對象 obj1.isPrototypeOf(obj2)
propertyIsEnumerable 方法 先判斷屬性是否屬于對象本身,如果不是,返回false,如果是,就繼續(xù)判斷屬性是否可以被遍歷,如果是才返回ture 反之則false
toString toLocaleString 方法 轉(zhuǎn)換成字符串 toLocaleString轉(zhuǎn)換成本地格式的字符串
valueOf 方法 當(dāng)對象參加運(yùn)算的時候,會首先調(diào)用valueOf方法獲取對象的值,如果獲取的值不能參與運(yùn)算,則調(diào)用toString方法
proto 屬性 指向?qū)ο箨P(guān)聯(lián)的原型對象
16.Function eval
都可以將字符串轉(zhuǎn)換成代碼
不同點(diǎn)
Function 創(chuàng)建出來的是函數(shù),不會直接調(diào)用,除非手動調(diào)用
eval 直接可以將字符串轉(zhuǎn)換成代碼,并執(zhí)行
17.arguments
函數(shù)內(nèi)部的一個對象,在函數(shù)調(diào)用的時候,系統(tǒng)會默認(rèn)的將所有傳入的實(shí)參存入該對象
arguments.length 表示傳入實(shí)參的個數(shù)
arguments.callee 指向當(dāng)前函數(shù) (匿名函數(shù)中使用,因?yàn)樗麤]有名字)
案例:歌曲管理器
- 在當(dāng)前對象的方法中,調(diào)用當(dāng)前對象中的另一個方法,需要使用this
遞歸
什么是遞歸
在程序中,所謂的遞歸,就是函數(shù)自己直接或間接的調(diào)用自己。調(diào)用自己分兩種:
直接調(diào)用自己;
間接調(diào)用自己。
就遞歸而言最重要的就是
跳出結(jié)構(gòu),因?yàn)樘隽瞬趴梢杂薪Y(jié)果。
化歸思想
化歸思想:將一個問題由難化易,由繁化簡,由復(fù)雜化簡單的過程稱為化歸,它是轉(zhuǎn)化和歸結(jié)的簡稱。
遞歸思想就是將一個問題轉(zhuǎn)換為一個已解決的問題來實(shí)現(xiàn)的。
假如有一個函數(shù)f,如果它是遞歸函數(shù)的話,,那么也就是說函數(shù)體內(nèi)的問題還是轉(zhuǎn)換為f的形式。
function f() {
... f( ... ) ...
}
例子:
求1,2,3,4,5...100的和。
- 首先假定遞歸函數(shù)已經(jīng)寫好,假設(shè)是foo。即foo(100)就是求1到100的和;
- 尋找遞推關(guān)系,就是n與n-1,或n-2之間的關(guān)系:
foo(n) == n + foo( n - 1 )
var res = foo(100);
var res = foo(99) + 100;
- 將遞推結(jié)構(gòu)轉(zhuǎn)換為遞歸體
function foo(n){
return n + foo( n - 1 );
}
上面就是利用了化歸思想:
將求 100 轉(zhuǎn)換為 求 99;
將求 99 轉(zhuǎn)換為 求 98;
...
將求 2 轉(zhuǎn)換為 求 1;
求 1 結(jié)果就是 1;
即: foo( 1 ) 是 1。
將臨界條件加到遞歸體中(求1的結(jié)果為1)
function foo( n ) {
if ( n == 1 ) return 1;
return n + foo( n - 1 );
}
練習(xí):
一、求1,3,5,7,9,...第n項(xiàng)的結(jié)果與前n項(xiàng)和。序號從0開始
- 先求第n項(xiàng):
- 首先假定遞歸函數(shù)已經(jīng)寫好,假設(shè)是fn。 那么第n項(xiàng)就是fn(n)
- 找遞推關(guān)系:fn(n) == f(n-1) + 2
- 遞歸體:
function fn(n) {
return fn(n-1) + 2;
}
- 找臨界條件
求 n -> n-1
求 n-1 -> n-2
...
求 1 -> 0
求 第 0 項(xiàng), 就是 1 - 加入臨界條件:
function fn( n ) {
if ( n == 0 ) return 1;
return fn( n-1 ) + 2;
}
- 再看求前n項(xiàng)和
- 假設(shè)已完成:sum( n ) 就是前 n 項(xiàng)和;
- 找遞推關(guān)系:前n項(xiàng)和等于第n項(xiàng) + 前n-1項(xiàng)的和;
- 遞歸體
function sum( n ) {
return fn( n ) + sum( n - 1 );
}
- 找臨界條件:
- n == 1結(jié)果為 1;
- 加入臨界條件:
function sum( n ) {
if (n == 0) return 1;
return fn(n) + sum(n - 1);
}
二、Fibonacci數(shù)列:1,1,2,3,5,8,13,21,34,55,89...求其第n項(xiàng)。
- 遞推關(guān)系:fn(n) == fn(n-1) + fn(n - 2)
function fib( n ) {
if ( n == 0 || n == 1 ) return 1;
return fib( n - 1 ) + fib( n - 2 );
}
三、階乘:一個數(shù)字的階乘表示的是從 1 開始累乘到這個數(shù)字。例如:3!表示123。規(guī)定 0 沒有階乘, 階乘從1開始。
- 求n的階乘
function foo ( n ) {
if ( n == 1 ) return 1;
return foo( n - 1 ) * n;
}
案例:使用遞歸遍歷所有的后代元素:
- DOM沒有提供直接獲取后代元素的API;
- 但是可以通過childNode來獲取所有的子元素;
作用域
域,表示的是一個范圍,作用域,就是作用范圍。
作用域說明的是一個變量可以在什么地方被使用,什么地方不能被使用。
塊級作用域
- JavaScript中沒有塊級作用域
{
var num = 123;
{
console.log( num );
}
}
console.log( num );
- 上面這段代碼在JavaScript中是不會報(bào)錯的,但是在其他的編程語言中(C#、C、JAVA)會報(bào)錯。
- 這是因?yàn)?,在JavaScript中沒有塊級作用域,使用{}標(biāo)記出來的代碼塊中聲明的變量num,是可以被{}外面訪問到的。
- 但是在其他的編程語言中,有塊級作用域,那么{}中聲明的變量num,是不能在代碼塊外部訪問的,所以報(bào)錯。
詞法作用域
什么是詞法作用域?
詞法( 代碼 )作用域,就是代碼在編寫過程中體現(xiàn)出來的作用范圍。代碼一旦寫好,不用執(zhí)行, 作用范圍就已經(jīng)確定好了, 這個就是所謂詞法作用域。
在 js 中詞法作用域規(guī)則:
函數(shù)允許訪問函數(shù)外的數(shù)據(jù);
整個代碼結(jié)構(gòu)中只有函數(shù)可以限定作用域;
作用域規(guī)則首先使用提升規(guī)則分析;
如果當(dāng)前作用規(guī)則中有名字了,就不考慮外面的名字。
案例1:
var num = 123;
function foo() {
console.log( num );
}
foo();
- 案例2:
if ( false ) {
var num = 123;
}
console.log( num ); // undefiend
- 例子3:
var num = 123;
function foo() {
var num = 456;
function func() {
console.log( num );
}
func();
}
foo(); //456
- 例子4:
var num1 = 123;
function foo1() {
var num1 = 456;
function foo2() {
num1 = 789;
function foo3 () {
console.log( num1 );
}
foo3();
console.log( num1 );
}
foo2();
console.log( num1 );
}
foo1();
//789
//789
//789
- 面試題
var num = 123;
function func1(){
console.log(num);
}
function func2(){
var num = 456;
func1();
}
func2(); //123[詞法作用域]
變量提升
JavaScript是解釋型的語言,但是他并不是真的在運(yùn)行的時候逐句的往下解析執(zhí)行。
我們來看下面這個例子:
func();
function func(){
alert("Funciton has been called");
}
在上面這段代碼中,函數(shù)func的調(diào)用是在其聲明之前,如果說JavaScript代碼真的是逐句的解析執(zhí)行,那么在第一句調(diào)用的時候就會出錯,然而事實(shí)并非如此,上面的代碼可以正常執(zhí)行,并且alert出來Function has been called。
所以,可以得出結(jié)論,JavaScript并非僅在運(yùn)行時簡簡單單的逐句解析執(zhí)行!
JavaScript 預(yù)解析
JavaScript引擎在對JavaScript代碼進(jìn)行解釋執(zhí)行之前,會對JavaScript代碼進(jìn)行預(yù)解析,在預(yù)解析階段,會將以關(guān)鍵字var和function開頭的語句塊提前進(jìn)行處理。
關(guān)鍵問題是怎么處理呢?
當(dāng)變量和函數(shù)的聲明處在作用域比較靠后的位置的時候,變量和函數(shù)的聲明會被提升到作用域的開頭。
重新來看上面的那段代碼
func();
function func(){
alert("Funciton has been called");
}
- 由于JavaScript的預(yù)解析機(jī)制,上面的代碼就等效于:
function func(){
alert("Funciton has been called");
}
func();
- 看完函數(shù)聲明的提升,再來看一個變量聲明提升的例子:
alert(a);
var a = 1;
- 由于JavaScript的預(yù)解析機(jī)制,上面這段代碼,alert出來的值是undefined,如果沒有預(yù)解析,代碼應(yīng)該會直接報(bào)錯a is not defined,而不是輸出值。
- Wait a minute,不是說要提前的嗎?那不是應(yīng)該alert出來1,為什么是undefined?
- 那么在這里有必要說一下聲明、定義、初始化的區(qū)別。其實(shí)這幾個概念是C系語言的人應(yīng)該都比較了解的。
| 行為 | 說明 |
|---|---|
| 聲明 | 告訴編譯器/解析器有這個變量存在,這個行為是不分配內(nèi)存空間的,在JavaScript中,聲明一個變量的操作為:var a; |
| 定義 | 為變量分配內(nèi)存空間,在C語言中,一般聲明就包含了定義,比如:int a;,但是在JavaScript中,var a;這種形式就只是聲明了。 |
| 初始化 | 在定義變量之后,系統(tǒng)為變量分配的空間內(nèi)存儲的值是不確定的,所以需要對這個空間進(jìn)行初始化,以確保程序的安全性和確定性 |
| 賦值 | 賦值就是變量在分配空間之后的某個時間里,對變量的值進(jìn)行的刷新操作(修改存儲空間內(nèi)的數(shù)據(jù)) |
| 所以我們說的提升,是聲明的提升。 |
- 那么再回過頭看,上面的代碼就等效于:
var a; //這里是聲明
alert(a);//變量聲明之后并未有初始化和賦值操作,所以這里是 undefined
a = 1;
復(fù)雜點(diǎn)的情況分析
- 函數(shù)同名,觀察下面這段代碼:
func1();
function func1(){
console.log('This is func1');
}
func1();
function func1(){
console.log('This is last func1');
}
- 輸出結(jié)果為:
This is last func1
This is last func1
- 原因分析:由于預(yù)解析機(jī)制,func1的聲明會被提升,提升之后的代碼為:
function func1(){
console.log('This is func1');
}
function func1(){
console.log('This is last func1');
}
func1();
func1();
同名的函數(shù),后面的會覆蓋前面的,所以兩次輸出結(jié)果都是This is last func1。
變量和函數(shù)同名
alert(foo);
function foo(){}
var foo = 2;
- 當(dāng)出現(xiàn)變量聲明和函數(shù)同名的時候,只會對函數(shù)聲明進(jìn)行提升,變量會被忽略。所以上面的代碼的輸出結(jié)果為:
function foo(){}
- 解析之后的代碼:
function foo(){};
alert(foo);
foo = 2;
- 再來看一種
var num = 1;
function num () {
alert( num );
}
num();
- 代碼執(zhí)行結(jié)果為:
Uncaught TypeError: num is not a function
- 直接上預(yù)解析后的代碼:
function num(){
alert(num);
}
num = 1;
num();
預(yù)解析是分作用域的
- 聲明提升并不是將所有的聲明都提升到window對象下面,提升原則是提升到變量運(yùn)行的環(huán)境(作用域)中去。
function showMsg()
{
var msg = 'This is message';
}
alert(msg); // msg未定義
- 把預(yù)解析之后的代碼寫出來:
function showMsg()
{
var msg;
msg = 'This is message';
}
alert(msg); // msg未定義
- 分作用域:
var msg = "aaa";
function showMsg()
{
alert(msg); // msg未定義
var msg = 'This is message';
}
var aaa = 10;
function f1() {
console.log(aaa);
aaa = 20;
}
console.log(aaa);
f1();
console.log(aaa);
//10
//10
//20
var aaa = 10;
function f1() {
console.log(aaa);
var aaa = 20;
}
console.log(aaa);
f1();
console.log(aaa);
//10
//undefined
//10
預(yù)解析是分段的
- 分段,其實(shí)就分script標(biāo)簽的
<script>
func(); // 輸出 AA2;
function func(){
console.log('AA1');
}
function func(){
console.log('AA2');
}
</script>
<script>
function func(){
console.log('AA3');
}
</script>
在上面代碼中,第一個script標(biāo)簽中的兩個func進(jìn)行了提升,第二個func覆蓋了第一個func,但是第二個script標(biāo)簽中的func并沒有覆蓋上面的第二個func。所以說預(yù)解析是分段的。
tip:但是要注意,分段只是單純的針對函數(shù),變量并不會分段預(yù)解析。
函數(shù)表達(dá)式并不會被提升
func();
var func = function(){
alert("我被提升了");
};
- 這里會直接報(bào)錯,func is not a function,原因就是函數(shù)表達(dá)式,并不會被提升。只是簡單地當(dāng)做變量聲明進(jìn)行了處理,如下:
var func;
func();
func = function(){
alert("我被提升了");
}
練習(xí):
if("a" in window){
var a = 10;
}
alert(a); //10
- 上述代碼預(yù)解析:
var a;
if("a" in window){
a = 10;
}
alert(a);
function fn(){
if("a" in window){
var a = 10;
}
alert(a);
}
fn(); //undefined
var foo = 1;
function bar(){
if(!foo){
var foo = 10;
}
alert(foo); //10
}
bar();
條件式函數(shù)聲明
console.log(typeof func);
if(true){
function(){
return 1;
}
}
console.log(typeof func);
上面這段代碼,就是所謂的條件式函數(shù)聲明,這段代碼在Gecko引擎中打印"undefined"、"function";而在其他瀏覽器中則打印"function"、"function"。
原因在于Gecko加入了ECMAScript以外的一個feature:條件式函數(shù)聲明。
說明:
Conditionally created functions Functions can be conditionally declared, that is, a function declaration can be nested within an if statement.
Note: Although this kind of function looks like a function declaration, it is actually an expression (or statement), since it is nested within another statement. See differences between function declarations and function expressions.
Note中的文字說明,條件式函數(shù)聲明的處理和函數(shù)表達(dá)式的處理方式一樣,所以條件式函數(shù)聲明沒有聲明提升的特性。
復(fù)習(xí):
作用域:變量的作用范圍;
js中的作用域是詞法作用域:代碼在寫好之后,變量的作用域已經(jīng)確定;
js中沒有塊級作用域。
js中只有函數(shù)可以創(chuàng)建作用域;
變量提升:在分析代碼的時候,首先將以var聲明的變量和function聲明的函數(shù)進(jìn)行提升;再去執(zhí)行代碼的具體執(zhí)行過程:
變量的提升是分作用域的;
當(dāng)函數(shù)和變量名相同的時候,只提升函數(shù),不提升變量;
函數(shù)名相同,全部都會被提升,后面的函數(shù)會覆蓋前面的函數(shù);
函數(shù)表達(dá)式中函數(shù)不會被提升,但是變量會被提升。
func();
var func = function () {
console.log(11111);
}
- 上述代碼執(zhí)行會報(bào)錯,變量提升如下:
var func;
func();
func = function () {
console.log(11111);
}
- 如下代碼就不會報(bào)錯:
var func = function () {
console.log(11111);
};
func();
- 并不是函數(shù)內(nèi)部寫了變量,這個變量就屬于這個函數(shù)的作用域,而是必須使用var來聲明的變量才屬于這個函數(shù)作用域。
作用域鏈
什么是作用域鏈
只有函數(shù)可以制造作用域結(jié)構(gòu),那么只要是代碼,就至少有一個作用域,即全局作用域。
凡是代碼中有函數(shù),那么這個函數(shù)就構(gòu)成另一個作用域。如果函數(shù)中還有函數(shù),那么在這個作用域中就又可以誕生一個作用域。
將這樣的所有的作用域列出來,可以有一個結(jié)構(gòu):函數(shù)內(nèi)指向函數(shù)外的鏈?zhǔn)浇Y(jié)構(gòu)。就稱作作用域鏈。
函數(shù)內(nèi)部的作用域可以訪問函數(shù)外部的作用域;
如果有多個函數(shù)嵌套,那么就會構(gòu)成作用域鏈。
- 例如:
function f1() {
function f2() {
}
}
//f2-->f1-->全局
var num = 456;
function f3() {
function f4() {
}
}
//f4-->f3-->全局
- 繪制作用域鏈的步驟:
- 看整個全局是一條鏈,即頂級鏈,記為 0 級鏈;
- 看全局作用域中,有什么成員聲明,就以方格的形式繪制到 0 級練上;
- 再找函數(shù),只有函數(shù)可以限制作用域,因此從函數(shù)中引入新鏈,標(biāo)記為 1 級鏈;
- 然后在每一個 1 級鏈中再次往復(fù)剛才的行為。
變量的訪問規(guī)則
首先看變量在第幾條鏈上;在當(dāng)前鏈上看是否有變量的定義與賦值,如果有直接使用;
如果沒有到上一級鏈上找( n - 1 級鏈 ), 如果有直接用,停止繼續(xù)查找;
如果還沒有再次往上剛找... 直到全局鏈( 0 級 ),還沒有就是 is not defined。
注意,同級的鏈不可混合查找。
練習(xí)1:繪制作用域鏈
function f1() {
var num = 123;
function f2() {
console.log( num );
}
f2();
}
var num = 456;
f1(); //123
- 練習(xí)2:
var num = 456;
function f() {
num = 678;
function foo() {
var num = 999;
console.log(num);
}
foo();
console.log(num);
}
f(); //999 678
- 練習(xí)3:變量提升會提升到函數(shù)前面
function fff() {
console.log(num);
}
fff(); //undefined
var num = 123;
function fff() {
console.log(num);
}
var num = 123;
fff(); //123
如何分析代碼
- 在分析代碼的時候切記從代碼的運(yùn)行進(jìn)度上來分析,如果代碼給變量賦值了,一定要標(biāo)記到圖中;
- 如果代碼比較復(fù)雜,可以在圖中描述代碼的內(nèi)容,有事甚至需要將原型圖與作用域圖合并分析。
練習(xí)
- 第一題:
var num = 123;
function f1() {
console.log( num );
}
function f2() {
var num = 456;
f1();
}
f2(); //123
- 第二題:
var num = 123;
function f1() {
console.log( num );
}
function f2() {
num = 456;
f1();
}
f2(); //456
補(bǔ)充
聲明變量使用
var,如果不使用var聲明的變量就是全局變量(禁用);因?yàn)樵谌魏未a結(jié)構(gòu)中都可以使用該語法。 那么再代碼維護(hù)的時候會有問題,所以除非特殊原因不要這么用。
下面的代碼的錯誤
function foo () {
var i1 = 1 // 局部
i2 = 2, // 全局
i3 = 3; // 全局
}
- 此時注意:
var arr = [];
for ( var i = 0; i < 10; i++ ) {
arr.push( i );
}
for ( var i = 0; i < 10; i++ ) {
console.log( arr[ i ] );
} //0 1 2 3 4 5 6 7 8 9
// 一般都是將變量的聲明全部放到開始的位置,避免出現(xiàn)因?yàn)樘嵘斐傻腻e誤
var arr = [],
i = 0;
for ( ; i < 10; i++ ) {
arr.push( i );
}
for ( i = 0; i < 10; i++ ) {
console.log( arr[ i ] );
} //0 1 2 3 4 5 6 7 8 9
閉包
閉包的概念
- 閉包從字面意思理解就是閉合,包起來。
- 簡單的來說閉包就是,一個具有封閉的對外不公開的包裹結(jié)構(gòu)或空間。
- 在JavaScript中函數(shù)可以構(gòu)成閉包。一般函數(shù)是一個代碼結(jié)構(gòu)的封閉結(jié)構(gòu),即包裹的特性,同時根據(jù)作用域規(guī)則,只允許函數(shù)訪問外部的數(shù)據(jù),外部無法訪問函數(shù)內(nèi)部的數(shù)據(jù),即封閉的對外不公開的特性。因此說函數(shù)可以構(gòu)成閉包。
閉包要解決什么問題?
- 閉包內(nèi)的數(shù)據(jù)不允許外界訪問;
- 要解決的問題就是間接訪問該數(shù)據(jù)。
訪問數(shù)據(jù)的問題
- 我們觀察下面的函數(shù)foo,在foo內(nèi)部有一個變量num,能否在函數(shù)外部訪問到這個變量num呢?
function foo () {
var num = 123;
return num;
}
var res = foo();
console.log( res ); // => 123
分析:
在上面的代碼中,確實(shí)可以訪問到num這個函數(shù)內(nèi)部的變量。但是能不能多次訪問呢?
不能,因?yàn)槊看卧L問都得重新調(diào)用一次foo函數(shù),每次調(diào)用都會重新創(chuàng)建一個num = 123,然后返回。
解決思路
函數(shù)內(nèi)的數(shù)據(jù)不能直接在函數(shù)外被訪問,是因?yàn)樽饔糜虻年P(guān)系,上級作用域不能直接訪問下級作用域中的數(shù)據(jù)。
但是如果反過來,下級作用域可以直接訪問上級作用域中的數(shù)據(jù)。那么如果在函數(shù)foo內(nèi)定義一個函數(shù),那么在這個內(nèi)部函數(shù)中是可以直接訪問foo中的num的。
function foo() {
var num = Math.random();
function func() {
return num;
}
return func;
}
var f = foo();
// f可以直接訪問num,而且多次訪問,訪問的也是同一個,并不會返回新的num
var res1 = f();
var res2 = f();
如何獲得超過一個數(shù)據(jù)
- 函數(shù)的返回值只能有一個,那按照上面的方法,我們只能對函數(shù)內(nèi)部的一個數(shù)據(jù)進(jìn)行操作。怎么操作函數(shù)內(nèi)的多個數(shù)據(jù)呢?
- 可以使用對象,代碼如下:
function foo () {
var num1 = Math.random();
var num2 = Math.random();
//可以將多個函數(shù)包含在一個對象內(nèi)進(jìn)行返回,這樣就能在函數(shù)外部操作當(dāng)前函數(shù)內(nèi)的多個變量
return {
num1: function () {
return num1;
},
num2: function () {
return num2;
}
}
}
如何完成讀取一個數(shù)據(jù)和修改這個數(shù)據(jù)
- 前面講的都是如何去獲取函數(shù)內(nèi)部的數(shù)據(jù),接下來我們考慮如何修改函數(shù)內(nèi)部的數(shù)據(jù)。
- 同樣,也是使用內(nèi)部的函數(shù)進(jìn)行操作。
function foo() {
var num = Math.random();
//分別定義get和set函數(shù),使用對象進(jìn)行返回
return {
//get_num負(fù)責(zé)獲取數(shù)據(jù)
get_num: function() {
return num;
},
//set_num負(fù)責(zé)設(shè)置數(shù)據(jù)
set_num: function(value) {
num = value;
}
}
}
閉包的基本結(jié)構(gòu)
- 一般閉包要解決的的問題就是要想辦法間接的獲得函數(shù)內(nèi)數(shù)據(jù)的使用權(quán)。那么我們就可以總結(jié)出一個基本的使用模型:
- 寫一個函數(shù),函數(shù)內(nèi)定義一個新函數(shù),返回新函數(shù),用新函數(shù)獲得函數(shù)內(nèi)的數(shù)據(jù);
- 寫一個函數(shù),函數(shù)內(nèi)定義一個對象,對象中綁定多個函數(shù)( 方法 ),返回對象,利用對象的方法訪問函數(shù)內(nèi)的數(shù)據(jù)。
閉包的作用
閉包的基本作用:可以通過閉包返回的函數(shù)或者方法,來修改函數(shù)內(nèi)部的數(shù)據(jù)。
- 在函數(shù)外部想要修改數(shù)據(jù),只能通過函數(shù)內(nèi)部的方法;
- 我們可以在函數(shù)內(nèi)部定義的這個方法里,設(shè)置安全措施,比如檢驗(yàn)之類的操作,可以保證系統(tǒng)的安全性和穩(wěn)定性。
復(fù)習(xí)
使用遞歸獲取后代元素
作用域
變量起作用的范圍;
什么是塊級作用域:
JS中沒有塊級作用域,使用代碼塊限定的作用域就是塊級作用域。
JS中的作用域叫做詞法作用域:
在代碼寫好的時候,就能確定變量的作用域叫詞法作用域。
動態(tài)作用域(是詞法作用域就不可能是動態(tài)作用域)。
在JS中,只有函數(shù)能創(chuàng)造作用域。
變量提升
JS代碼的運(yùn)行分兩個階段:
預(yù)解析階段:變量名和函數(shù)名提升(將var聲明的變量和function聲明的函數(shù)提升到當(dāng)前作用域的最上方);
執(zhí)行階段。
注:
變量名和函數(shù)名相同的時候,只提升函數(shù)名,不提升變量名;
函數(shù)名相同的時候,都提升,但是后面的函數(shù)會覆蓋前面的函數(shù);
函數(shù)表達(dá)式,只會提升變量名,不會提升后面的函數(shù);
變量提升只會將變量和函數(shù)提升到當(dāng)前作用域的最上方。
變量提升是分塊
<script></script>的。條件式函數(shù)聲明是否會被提升,取決于瀏覽器,不推薦去寫
foo(); //報(bào)錯
if(true){
function foo(){
console.log("123");
}
}
foo(); //123
作用域鏈
- 只要是函數(shù)都有作用域,函數(shù)內(nèi)部的作用域都可以訪問函數(shù)外部的作用域,當(dāng)多個函數(shù)嵌套的時候,就會形成一個鏈?zhǔn)降慕Y(jié)構(gòu),這個結(jié)構(gòu)就是作用域鏈。
繪制作用域鏈圖的步驟
- 先繪制0級作用域鏈;
- 在全局作用域中查找變量和函數(shù)的聲明,找到之后將所有的變量和函數(shù)用小方格放在0級作用域上;
- 在0級作用域鏈上的函數(shù)引出1級作用域鏈;
- 再去每一個1級作用域鏈中查找變量和函數(shù)的聲明,找到之后...
- 以此重復(fù),就畫好了整個作用域鏈。
變量搜索規(guī)則
- 首先在訪問變量的作用域中查找該變量,如果找到就直接使用;
- 如果沒有找到,就去上一級作用域中繼續(xù)查找,如果找到就直接使用;
- 如果沒有找到,就繼續(xù)去上一級作用于中查找,知道找到0級為止;
- 如果找到了就用,如果沒有找到就undefined(變量)或者報(bào)錯(函數(shù))。
閉包
- 閉包是一個封閉的對外不公開的包裹結(jié)構(gòu)或者空間;
- JS中的閉包是函數(shù);
- 閉包要解決的問題:在函數(shù)外部訪問不到函數(shù)內(nèi)部的數(shù)據(jù);要解決的問題就是需要在函數(shù)外部間接的訪問函數(shù)內(nèi)部的數(shù)據(jù)。
閉包的基本結(jié)構(gòu)
- 返回一個數(shù)據(jù):
function outer(){
var data = "數(shù)據(jù)";
return function(){
return data;
}
}
- 返回多個數(shù)據(jù):
function outer(){
var data1 = "數(shù)據(jù)1";
var data2 = "數(shù)據(jù)2";
return {
getData1:function(){
return data1;
},
setData1:function(value){
data1 = value;
return data1;
},
getData2:function(){
return data2;
},
setData2:function(value){
data2 = value;
return data2;
}
}
}
閉包的作用
- 如果把數(shù)據(jù)放在全局作用域內(nèi),那么所有人都可以隨意修改,這樣數(shù)據(jù)就不再可靠。
- 閉包可以創(chuàng)建一個私有的空間,在這個空間內(nèi)部的數(shù)據(jù),外部無法直接訪問;
- 外部空間想要訪問函數(shù)內(nèi)部的數(shù)據(jù),只能通過閉包提供的指定方法,在這個指定方法內(nèi)部可以設(shè)置一些校驗(yàn)規(guī)則,讓數(shù)據(jù)變得更加安全;
函數(shù)模式
特征:就是一個簡單的函數(shù)調(diào)用,函數(shù)名前面沒有任何的引導(dǎo)內(nèi)容
function foo(){}
var func = function(){}
foo();
func();
(function(){})();
this在函數(shù)模式中的含義: this在函數(shù)中表示全局對象,在瀏覽器中是window對象
方法模式
特征: 方法一定是依附于一個對象, 將函數(shù)賦值給對象的一個屬性, 那么就成為了方法.
function f() {
this.method = function () {};
}
var o = {
method: function () {}
}
this在方法模式調(diào)用中的含義:表示函數(shù)所依附的這個對象
構(gòu)造器調(diào)用模式
由于構(gòu)造函數(shù)只是給 this 添加成員. 沒有做其他事情. 而方法也可以完成這個操作, 就 this 而言, 構(gòu)造函數(shù)與方法沒有本質(zhì)區(qū)別.
特征:使用 new 關(guān)鍵字, 來引導(dǎo)構(gòu)造函數(shù).
function Person(){
this.name = "zhangsan";
this.age = 19;
this.sayHello = function(){
};
}
var p = new Person();
構(gòu)造函數(shù)中發(fā)this與方法中一樣, 表示對象, 但是構(gòu)造函數(shù)中的對象是剛剛創(chuàng)建出來的對象
關(guān)于構(gòu)造函數(shù)中return關(guān)鍵字的補(bǔ)充說明
構(gòu)造函數(shù)中不需要return, 就會默認(rèn)的return this
如果手動的添加return, 就相當(dāng)于 return this
如果手動的添加return 基本類型; 無效, 還是保留原來 返回this
如果手動添加return null; 或return undefiend, 無效
如果手動添加return 對象類型; 那么原來創(chuàng)建的this就會被丟掉, 返回的是 return后面的對象
創(chuàng)建對象的模式
工廠方法
// 工廠就是用來生產(chǎn)的, 因此如果函數(shù)創(chuàng)建對象并返回, 就稱該函數(shù)為工廠函數(shù)
function createPerson( name, age, gender ) {
var o = {};
o.name = name;
o.age = age;
o.gender = gender;
return o;
}
// document.createElement()
構(gòu)造方法
function Person(name, age, gender){
this.name = name;
this.age = age;
this.gender = gender;
}
var p = new Person("zhangsan", 19, "男");
寄生式創(chuàng)建對象
function Person(name, age, gender){
var o = {};
o.name = name;
o.age = age;
o.gender = gender;
return o;
}
var p = new Person("Jack", 18, "male");
混合式創(chuàng)建
混合式繼承就是講所有的屬性放在構(gòu)造方法里面,然后講所有的方法放在原型里面,使用構(gòu)造方法和原型配合起來創(chuàng)建對象。
上下文調(diào)用模式
上下文(Context),就是函數(shù)調(diào)用所處的環(huán)境。
上下文調(diào)用,也就是自定義設(shè)置this的含義。
在其他三種調(diào)用模式中,函數(shù)/方法在調(diào)用的時候,this的值都是指定好了的,我們沒辦法自己進(jìn)行設(shè)置,如果嘗試去給this賦值,會報(bào)錯。
上下文調(diào)用的語法
//第一種, apply
函數(shù)名.apply(對象, [參數(shù)]);
//第二種, call
函數(shù)名.call(對象, 參數(shù));
//上面兩種方式的功能一模一樣,只是在傳遞參數(shù)的時候有差異。
功能描述:
語法中的函數(shù)名表示的就是函數(shù)本身,使用函數(shù)調(diào)用模式的時候,this默認(rèn)是全局對象
語法中的函數(shù)名也可以是方法(如:obj.method),在使用方法模式調(diào)用的時候,this默認(rèn)是指當(dāng)前對象
在使用apply和call的時候,默認(rèn)的this都會失效,this的值由apply和call的第一個參數(shù)決定
補(bǔ)充說明
如果函數(shù)或方法中沒有this的操作, 那么無論什么調(diào)用其實(shí)都一樣.
如果是函數(shù)調(diào)用foo(), 那么有點(diǎn)像foo.apply( window ).
如果是方法調(diào)用o.method(), 那么有點(diǎn)像o.method.apply( o ).
參數(shù)問題
call和apply在沒有后面的參數(shù)的情況下(函數(shù)無參數(shù), 方法無參數(shù)) 是完全一樣的.
如下:
function foo() {
console.log( this );
}
foo.apply( obj );
foo.call( obj );
第一個參數(shù)的使用規(guī)則:
如果傳入的是一個對象, 那么就相當(dāng)于設(shè)置該函數(shù)中的 this 為參數(shù)
如果不傳入?yún)?shù), 或傳入 null. undefiend 等, 那么相當(dāng)于 this 默認(rèn)為 window
foo();
foo.apply();
foo.apply( null );
foo.call( undefined );
如果傳入的是基本類型, 那么 this 就是基本類型對應(yīng)的包裝類型的引用
number -> Number
boolean -> Boolean
string -> String
第二個參數(shù)的使用規(guī)則
在使用上下文調(diào)用的時候, 原函數(shù)(方法)可能會帶有參數(shù), 那么這個參數(shù)在上下文調(diào)用中使用第二個( 第 n 個 )參數(shù)來表示
function foo( num ) {
console.log( num );
}
foo.apply( null, [ 123 ] );
// 等價于
foo( 123 );
上下文調(diào)用模式的應(yīng)用
上下文調(diào)用只是能修改this, 但是使用的最多的地方上是函數(shù)借用.
- 將偽數(shù)組轉(zhuǎn)換為數(shù)組
傳統(tǒng)的做法:
var a = {};
a[ 0 ] = 'a';
a[ 1 ] = 'b';
a.length = 2;
// 使用數(shù)組自帶的方法 concat
// 如果參數(shù)中有數(shù)組會把參數(shù)數(shù)組展開
// 語法: arr.concat( 1, 2, 3, [ 4, [ 5 ] ] );
// 特點(diǎn):不修改原數(shù)組
var arr = [];
var newArr = arr.concat( a );
由于a是偽數(shù)組, 只是長得像數(shù)組. 所以上面的代碼不能成功,不能使用concat方法。
但是apply方法有一個特性, 可以將數(shù)組或偽數(shù)組作為參數(shù)。(IE8不支持偽數(shù)組操作)
foo.apply( obj, 偽數(shù)組 ); // IE8 不支持
利用apply方法,可以寫出以下
//將偽數(shù)組 a 作為 apply 的第二個參數(shù)
var newArr = Array.prototype.concat.apply( [], a )
處理數(shù)組轉(zhuǎn)換, 實(shí)際上就是將元素一個一個的取出來構(gòu)成一個新數(shù)組, 凡是涉及到該操作的方法理論上都可以。
push方法
//用法:
arr.push( 1 ); //將這個元素加到數(shù)組中, 并返回所加元素的個數(shù)
arr.push( 1, 2, 3 ); //將這三個元素依次加到數(shù)組中, 返回所加個數(shù)
var a = { length: 0 }; // 偽數(shù)組
a[ a.length++ ] = 'abc'; // a[ 0 ] = 'abc'; a.length++;
a[ a.length++ ] = 'def';
// 使用一個空數(shù)組, 將元素一個個放到數(shù)組中即可
var arr = [];
arr.push( a ); // 此時不會將元素展開, 而是將這個偽數(shù)組作為一個元素加到數(shù)組中
// 再次利用 apply 可以展開偽數(shù)組的特征
arr.push.apply( arr, a );
// 利用 apply 可以展開偽數(shù)組的特性, 這里就相當(dāng)于 arr.push( a[0], a[1] )
- 求數(shù)組中的最大值
傳統(tǒng)的做法
var max = arr[ 0 ];
for ( var i = 1; i < arr.length; i++ ) {
if ( arr[ i ] > max ) {
...
}
}
在 js 中的Math對象中提供了很多數(shù)學(xué)函數(shù)Math.max( 1,2,3 )
還是利用 apply 可以展開數(shù)組的特性
var arr = [ 123456,12345,1234,345345,234,5 ];
Math.max.apply( null, arr );
3.借用構(gòu)造函數(shù)繼承
function Person ( name, age, gender ) {
this.name = name;
this.age = age;
this.gender = gender;
}
// 需要提供一個 Student 的構(gòu)造函數(shù)創(chuàng)建學(xué)生對象
// 學(xué)生也應(yīng)該有 name, age, gender, 同時還需要有 course 課程
function Student ( name, age, gender, course ) {
Person.call( this, name, age, gender );
this.course = course;
}