面向對象幾特點:

  • OOP面向對象
  • 抽象:抓核心問題;
  • 封裝:只能通過對象來訪問方法
  • 繼承:從已有對象上繼承出新的對象 //改特殊部分
  • 多態:多對象的不同形態 一個usb接口提供不同功能

對象組成:方法(行為,操作) 函數:過程,動態的
屬性:變量,狀態,靜態;

var arr = []
arr.number = 10;
/* 對象下面的變量叫對象的屬性;*/
console.log(arr.number);
console.log(arr.length);
arr.test = function () { //對象下面的函數叫對象的方法;
    alert(123)
}
arr.test()

//加括號是方法,不加括號是屬性;

    var obj = new Object(); //空對象
    obj.name = '小明';
    obj.show = function () {
        console.log(this.name);
    }
    obj.show()

为对象添加属性和方法
Object对象
this指向
创建两个对象 : 重复代码过多



工廠模式; === 面向對象中的封裝函數
理论上的状态:

    function cPerson(name){
        //原料
        var obj = new Object()
        //加工
        obj.name = name;
        obj.showName = function () {
            console.log(this.name);
        }
        //出廠
       return obj;
    }
   var p1 =  cPerson('小明')
    p1.showName()
    var p2 = cPerson('小強')
    p2.showName()

當new去調用一個函數,這裏函數中的this == 創建出來的對象;而且函數的返回值直接即是this; 隱式返回;
所以問題是:this在哪? 原料是什麼,怎麼加工,出廠後返回值是什麼?

    function CPerson(name){
        //this哪?
        //原料
        //加工
        this.name = name;
        this.showName = function () {
            console.log(this.name);
        }
        //出廠
       // return obj;   //正常的返回沒有return會返回undefined; 如果是用new調用的話,則是返回this;
    }

創建2對象:

var p1 = new CPerson('小強');
var p2 = new CPerson('小明')

    console.log(p1.showName);
    console.log(p2.showName);
    console.log(p1.showName == p2.showName); //false;
//為什麼得false?
//問題:因為引用地址不相同,所以比如創建2000個對象,
//即會有2000個地址,所以必須使用引用相同,變為真;,所以是原型 ;

基本類型比較,對象賦值

    var a = 5 ;
    var b = 5;
    console.log(a === b); //基本類型的比較,只要值相同就行;true;

    var a = [1,2,3];
    var b = [1,2,3];
    console.log(a == b);  //對象 值相同,引用地址不同相同,所以是false;

    var a = [1,2,3]
    var b = a;
    console.log(a == b);   //值相同,引用相同 true;

    var a = [1,2,3]
    var b = a;//對象,把a的值跟地址都給了b 剪切
    b.push(4)
    console.log( b ) //[1,2,3,4]
    console.log( a ) //[1,2,3,4] 

    var a = [1,2,3]
    var b = a;//對象,把a的值跟地址都給了b 剪切
    b = [1,2,3,4]
    console.log( b ) //[1,2,3,4]
    console.log( a ) //[1,2,3] //出現= 必然在內存重新生成地址,所以不會改變a 

原型:原型去改寫對象下面公用的方法或屬性 ;讓公用的方法或屬性在內在中只存在一份,提高性能;

//原型 == class
//普通 == style

自行定義arr.sum方法

     var arr = [1,2,3,4,5]
     var arr2 = [2,3,34,4,23]
     arr.sum = function () {
     var result = 0;
     for(var i=0;i<this.length;i++){
     result += this[i]
     }
     return result;
     }
     console.log(arr.sum())

//原型prototype,寫在構造函數的下面 ;

    var arr = [1,2,3,4,5];
    var arr2 = [2,3,34,4,23]
    Array.prototype.sum = function () {
        var result = 0;
        for(var i=0;i<this.length;i++){
            result += this[i]
        }
        return result;
    }

    console.log(arr.sum());
    console.log(arr2.sum());

    var arr3 = []
    arr3.number = 10; //style
    Array.prototype.number = 20;   //class
    console.log(arr3.number);  //style優先級> class,所以彈出的10 ;



如何把過程寫法改為原型寫法;

 1、原型保存那些不變即公用的屬性及方法;屬性是變化的,則不能放到原型上,所以放到構造函數裏;
 2、
         function 構造函數(){
             this.屬性
          }
         構造函數.原型.方法 = function () {

          }
          var 對象1 = new 構造函數();
          對象1.方法()

總結:相同的属性和方法可以加载原型上,混合的编程模式,构造函数加属性,原型加方法



改寫tab選項卡:

1、不要函數套函數

2、可以有全局變量;

3、把onload中不是賦值的語句放到單獨的函數中;
改成面向對象:全局變量就是屬性;函數就是方法;onload中創建對象;改this指向(在事件或定時器時this指向易被改 )

function Tab(id) {
    this.box = document.getElementById(id);
    this.btn = this.box.getElementsByTagName('input')
    this.div = this.box.getElementsByTagName('div')
    this.num = 0;
}
Tab.prototype.init = function () {
    var _this = this;
    for (var i = 0; i < this.btn.length; i++) {
        this.btn[i].index = i;
        this.btn[i].onclick = function () {
            _this.change(this)  // //this是按鈕的this  _this是對像,即change由對象調用,所以下面的this不換;正確了
        };
    }
}
Tab.prototype.change = function (obj) { //change是在init調用,所以this指向init裏,必須修改
    for (var j = 0; j < this.btn.length; j++) {
        this.btn[j].className = ''
        this.div[j].style.display = 'none'
    }
    obj.className = 'on'  //傳進來按鈕的this
    this.div[obj.index].style.display = 'block' //傳進來按鈕的this
}
Tab.prototype.autoplay = function () {
    var _this = this;
    setInterval(function () {
        if (_this.num == _this.btn.length - 1) {
            _this.num = 0;
        }
        else {
            _this.num++
        }

        for (var j = 0; j < _this.btn.length; j++) {
            _this.btn[j].className = ''
            _this.div[j].style.display = 'none'
        }
        _this.btn[_this.num].className = 'on'  //傳進來按鈕的this
        _this.div[_this.num].style.display = 'block' //傳進來按鈕的this
    }, 1000)

}
<div id="box">
    <input type="button" value="1" class="on"/>
    <input type="button" value="2"/>
    <input type="button" value="3"/>
    <div style="display: block;">0001</div>
    <div>0002</div>
    <div>0003</div>
</div>

ev只能出現在事件函數中;return false 也要出現在事件函數中

  ---
    this._div.onmousedown = function(){
    This.fnDown()
    }
  --
    Drag.prototype.fnDown = function(ev){
        var ev = ev || event; 

    return false 
    }

//以上報錯;正確是:

  ---
    this._div.onmousedown = function(ev){ //事件函數
    var ev = ev || event; 
    This.fnDown(ev)
    return false 
    }
  --
    Drag.prototype.fnDown = function(ev){

    }



包裝對象;

    //包装对象 ;
    var str = 'hello'  //是类型,不是对像
    str.charAt(0)   //当调用方法时,基本类型会找到对象的包装对象类型,然后包装把所有的属性方法都给基本类型,然后包装对象消失;
    //包装对象送快递,送完后即有里面的东西
    str.indexOf('e')

//所以問題是:    console.log(typeof str); //string;不是对象;
//字符串怎么有方法 ? charAt(0)?
基本类型都有自己对应的包装对象:String Number Boolean; Array Date对象中的构造函数;这些类型怎么创建对象 ;

var str2 = new String('hello')
console.log(typeof str2) //這裏是obj了,即通過new創建的都是對象;
console.log(str.charAt(1))//所以有對象;

//String.prototype.chatAt = function(){}


當
var str = 'hello'  //這裏是字符串

str.charAt(0); //當字符串調方法時:基本類型會找到對應的包裝對像string對像,string下有原型方法 string.protype.charAt = function() {};

如何給字符串加方法?

String.prototype.lastValue = function(){
    return this.chatAt(this.lenght - 1)
}
var str3 = 'hello'
console.log( str3.laseValue() ) //o

注意:

var str = 'hello'
str.number = 10 ; //通过包装对象string添加,加完后包装对象消失

alert(str.number) //所以这里弹undefined ,如须使有效果须加在原型上:String.prototype.number = 10

如何給基本類型加屬性?
基本类型要加一个属性,到这里程序会找包装对象,然后在包装对象里创建一个对象,包装对象消失;然后再调用 str4.number时,其它会重新创建又一个对象,所以找不到是undefined; (没有加到原型里的情况;)

var str4  = 'hello'; 
str4.number = 10;
console.log(str4.number) //undefined;

當然如果通過new創建則是10,或直接加到原型 String.prototype.number = 10,也是10


原型鏈 _proto_ :【实例对象】与【原型】之间的连接,叫原型链
当a1调用num,基本a1是找不到的,找不到怎样----》再找原型链;_proto_

原型链的最外层是object;

    //实例对象与原型之间的连接,叫原型链
    function Aaa() {
        // this.num = 20;
    }
    //   Aaa.prototype.num = 10;  //Aaa.prototype即一个对象    //num即原型对象下的一个属性
    var a1 = new Aaa();                //实例对象
    Object.prototype.num = 30            
    console.log(a1.num);                                //之间的链接



面向對象的屬性:

hasownprototy:判斷屬性是否屬於 對象自身下面的屬性 [只有自己有,別人不能有的]

var arr = [];
    arr.num = 10;
Array.prototype.num2 = 20;
console.log(arr.hasOwnproperty('num')) //true;
console.log(arr.hasOwnproperty('num2')) //false,不是arr數組獨有;
function Bbb(){}

var b1 = new Bbb();
console.log(b1.hasOwnProoerty) ///究竟在哪;在原型链接下,object.prototype上,在顶部上;

console.log(b1.hasOwnProperty == Object.prototype.hasOwnProperty ) //true;

constructor: 查看對象構造函數,即由哪個函數構造出來的實例;

function Aaa(){}
var a1 = new Aaa();
console.log( a1.constructor ) //彈出本身的屬性,a1由哪個函數造出來的; function Aaa(){}

var arr2 = []
console.log( arr2.constructor ) //function Array() { [native code] } //私有方法看不到;

可以用來作判斷:

console.log( arr2.constructor == Array )  //true;


constructor怎麼來的?

當寫一個構造函數function Aaa(){ }
寫完後,即會自動生成: Aaa.prototype.constructor == Aaa 自動添加;

//當手動寫時,
var aa1 = new Aaa()
Aaa.prototype.constructor == Array; //一般不要去改,了解即可;

//則會自動生成的覆蓋了;
console.log(aa1.constructor == Array) //true;

<quoteblock>
只有constructor屬性才是程序自動生成的,每個函數都會生成,其他的不是 ;
</quoteblock>

//b1调用的其实不是自身的,而是最外层的;
console.log(b1.constructor.hasOwnProperty);


不經意改掉constructor情況;

function Ccc(){}
Ccc.prototype.num = 10;
Ccc.prototype.name = '小強'
Ccc.prototype.age = 20
console.log(c1.constructor); //function Ccc(){}

Ccc.prototype是一個對象,所以可寫成json形式:

Ccc.prototype = { //即把Ccc.prototype=  重新赋值,所以constructor会改
    name:'小強',
    age:20
}
var c1 = new Ccc();
console.log(c1.constructor)
 //function Object(){}找的就是json对应的constructor了,全覆盖上一条条自动生成的constructor

所以,constructor變成Object了,需要修正constructor指向,使之指回調用的函數;

    Ccc.prototype = {
        constructor : Ccc,
        name:'小明',
        age : 20
    }


for in 時,系統自動的屬性是for in不到的;
1、系統自添加的屬性;

function Ddd(){}
var d1 = new Ddd();
for(var attr in Ddd.prototype){
       console.log('能打印出來嗎?不能!')
}

2、自定義的屬性;

Ddd.prototype.name = 'abc';
for(var attr in Ddd.prototype){
        console.log('這裏是可打印出來的,即能找到;')
}

3、自定義的constructor屬性

Ddd.prototype.constructor = Aaa
for(var attr in Ddd.prototype){
        console.log(attr) //也不能打印出來;
}



instanceof運算符;
對象與構造函數在原型鏈接上是否有關係;

    function Eee(){}
    var e1 = new Eee()
    console.log(e1 instanceof Eee); //true;是否在同一个原型链接上;
    console.log(e1 instanceof Array); //false;是否在同一个原型链接上;
    console.log(e1 instanceof Object); //true;是否在同一个原型链接上;每一个对象最外层都是true; 任何对象 instanceof都为true;

所以,除constructor外;instanceof也可使類型判斷;

arr instanceof Array  //true;


类型判断三方法: constructor instanceof toString



問題:如何判斷一個變量是一個數組;
toString() Object方法;
toString在哪;系统对像下面都是自带的,自已写的对象都是通过原型链接找Object下面的;

var arr4 = []
console.log(arr4.toString) //可找到;function toString(){}
console.log(arr4.toString == Object.prototype.toString) //系統對象false;
function Fff(){}
var f1 = new Fff();
console.log(f1.toString) //toString在Object上;
console.log(f1.toString == Object.prototype.toString); //自已写的对象 true;

toString(),把对象转成字符串;

var arr5 = [1,2,3]
console.log(typeof arr5.toString())  //string
console.log(arr5.toString()); // [1,2,3]与数组一样的形式;

自己写可以需要写成自己的形式:

console.log('111' + typeof arr5);//还是Object,不会改变自身;
    Array.prototype.toString = function () {
        return this.join('+')
    }

    console.log(arr5.toString());
    '1+2+3'

進制轉換;

    var num6= 255;
    console.log(num6.toString(16)); //ff,转成16进制;
    console.log(num6.toString(2)); //11111111,转成2进制;

最重要的類型判斷;完美版;

var arr7 = []
    console.log(Object.prototype.toString.call(arr7)); //[object Array]
    var arr8 = {}
    console.log(Object.prototype.toString.call(arr8)); //[object Object]
    var arr9 = new Date()
    console.log(Object.prototype.toString.call(arr9)); //[object Date]
    var arr10 = new RegExp()
    console.log(Object.prototype.toString.call(arr10)); //[object RegExp]
    var arr11 = null;
    console.log(Object.prototype.toString.call(arr11)); //[object Null]

所以,判斷方法是:

console.log(Object.prototype.toString.call(arr7) == '[object Array]');

總結:判斷類型的三種方法,constructor,instanceof, toString.call()
前兩種在某些情況可能不行,如iframe

    window.onload = function () {
        var _f = document.createElement('iframe');
        document.body.appendChild(_f)
        var _farray = window.frames[0].Array //frames集合第0个,.Array找到iframe下面的Array,
        var arr = new _farray();//其实是跨页面了;

        console.log(arr.constructor == Array); //false 跨iframe里失效,但实际上确实是数组;
        console.log(arr instanceof Array); //false 也失效;
        console.log(Object.prototype.toString.call(arr) == '[object Array]');
        // true了;
    }