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