零JS筆記 | 面向對象 | 屬性方法

面向對象幾特點:

  • 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了;
    }

backbone 未入門

資源:

backbone中文檔


underscore中文檔

一个实现了web前端MVC模式的JS库
M : model (模型)
V : view (试图)
C : controller (控制器)
就是把模型与视图分离,通过控制器来连接他们
模塊:

Events : 事件驱动方法
Model : 数据模型
Collection : 模型集合器
Router : 路由器(hash)
History : 开启历史管理
Sync : 同步服务器方式
View : 试图(含事件行为和渲染页面)

1)直接創建對像:

/*
var models = new Backbone.Collection;
var views = new Backbone.View;
*/
var model = new Backbone.Model;
model.set('name','hello')
console.log(model.get('name'));

2)给构造函数添加实例方法和静态方法

    var m_1 = new Backbone.Model({'name':'hello'})
    var m_2 = new Backbone.Model({'name':'hi'})
    var coll = new Backbone.Collection()
    coll.add(m_1)
    coll.add(m_2)
    console.log(JSON.stringify(coll)); // 對象解析出字符串;

//Backbone.Model表示模型的構造函數,extend參數 ({json實例方法},{json靜態方法})

    var M = Backbone.Model.extend({
        aaa:function(){
            console.log(123);
        }
    },{
        bbb: function () {
            console.log(456);

        }
    })
    var model = new M
    model.aaa() //實例方法使用實例調用;
    M.bbb();  //靜態使用構造函數直接調用;即相當於多了一個命名空間而已;

//設置默認值:
    var M = Backbone.Model.extend({
        defaults:{
            name:'hello'
        }
    })
    var mo_= new M;
    console.log(mo_.get('name'));

3)继承操作

    var M = Backbone.Model.extend({
        aaa: function () {
            console.log('abc');
        }
    },{
        bbb: function () {
            console.log('def');
        }

    })
    var ChildM = M.extend();
    var mo = new ChildM
    mo.aaa()
    ChildM.bbb()

4)自定义事件

(function () {
    var M = Backbone.Model.extend({
        defaults:{
            name:'hello'
        },
        initialize:function(){
            this.on('change', function () {
                console.log('onchange觸發了');
            })
        }
    })
    var model = new M
    model.set('name','hi')
})();
//指定key值觸發;
(function () {
    var M = Backbone.Model.extend({
        defaults:{
            name:'hello'
        },
        initialize:function(){
            this.on('change:name', function (model) {  //不同的key值觸發;
                console.log(model);
            })
        }
    })
    var model = new M
    model.set('name','hi')
})();


//1、創建模型對像
//2、創建視圖對像,直接在視圖對象裏傳參,指定模型對像,即連接在一起;
//3、對模型修改時,即會觸發視圖對象;

var M = Backbone.Model.extend({
    defaults: {
        name: 'hello'
    }
});

var V = Backbone.View.extend({
    initialize: function () {
        this.listenTo(this.model, 'change', this.show);
    },
    show: function (model) {
        $('body').append('<div>' + model.get('name') + '</div>')
    }
});

var m = new M;          //1、創建模型對像
var v = new V({model: m});  //2、創建視圖對像,直接在視圖對象裏傳參,指定模型對像,即連接在一起;
m.set('name', 'hi')  //3、對模型修改時,即會觸發視圖對象;

5)數據與服務器


(function () {
    Backbone.sync = function (method, model) {
        console.log(method + ': ' + JSON.stringify(model));
        model.id = 1;
    }
    var M = Backbone.Model.extend({
        defaults:{
            name:'hello'
        },
        url:'/user' // ajax提交,一定要指定好url ;
    });
    var m = new M;
    m.save(); //把現在的模型對象保存到服務器上;
    m.save({name:'hi'});  //修改
})();

(function () {
    Backbone.sync = function (method, model) {
        console.log(method + ': ' + JSON.stringify(model));
    }
    var C = Backbone.Collection.extend({
        initialize: function () {
            this.on('reset', function () {
                console.log(123);
            })
        },
        url:'/user' // ajax提交,一定要指定好url ;
    });
    var m = new C;
    m.fetch() //讀取服務器數據;
})();

6)路由與歷史管理

(function () {
    var Workspace = Backbone.Router.extend({
        routes:{
            'help':             'help',
            'search/:query':    'search',  //#search/任意字符;
            'search/:query/p:page': 'search' //#search//kiwis/p7
        },
        help: function () {
            console.log(1);
        },
        search: function (query,page) {
            console.log(2);
        }

    })
    var w = new Workspace
    Backbone.history.start()
    /*
    * 地址後面加#help,彈1;
    * 地址後面加#search/dfk 彈2
    *
    * */
})();

7)事件委託

所有的视图都拥有一个 DOM 元素(el 属性),即使该元素仍未插入页面中去。 视图可以在任何时候渲染,然后一次性插入 DOM 中去,这样能尽量减少 reflows 和 repaints 从而获得高性能的 UI 渲染。 this.el 可以从视图的 tagName, className, id 和 attributes 创建,如果都未指定,el 会是一个空 div。

(function () {
    var V = Backbone.View.extend({
        el:$('body'), //觸發的委託人;
        events:{
            'click input':'aaa',
            'mouseover li':'bbb'
        },
        aaa: function () {
            console.log(1111111);
        },
        bbb: function () {
            console.log(2221222);
        }
    })
    var veiw = new V;
})();



8)前端模板

$(function () {

    var M = Backbone.Model.extend({
        defaults:{
            name:'hello'
        }
    })
    var V = Backbone.View.extend({
        initialize: function () {
            this.listenTo(this.model,'change',this.show)
        },
        show: function (model) {
            $('body').append(this.template(this.model.toJSON()))
        },
        template: _.template($('#template').html())
    })

    var m = new M;
    var v = new V({model:m})
    m.set('name','hdi')
})

調用:

<script type="text/template" id="template">
    <% for(var i=0;i<5;i++) { %>
    <div><%= name %></div>
    <% } %>
</script>


大部分操作:

首先頁面上先對【數據進行修改】 ----> 數據修改包括【模型修改】或【集合修改】--->

當這兩個修改後---> 會觸發相應的【自定義事件】--->

自定義事件找到相應---> 【視圖】--->

回調函數更新視圖----> 視圖的更新通過前端模板獲取到,再生成到---->頁面;

一個模型和一個集合,一個模型對應一個視圖,集合對應大視圖;


<script>
var f = 1;
document.getElementById('tdosclick').onclick = function(){
if(f){

document.getElementById('tdos').style.display = 'block';

}
else{
document.getElementById('tdos').style.display = 'none';
}
f = !f;
}
</script>