web前端開發筆記整理(四)

對象

JavaScript不區分類和實例的概念
創建一個name對象

var name=“小明”;

創建一個函數的時候,函數也是對象

function foo() {
    return 0;
}

創建對象的兩種方式

  • var obj = new object();
  • var obj = {};

寫個小demo感受一下:

var test={
	name:"xiaohua",
	age:"18",
	sex:"male",
	health:100,
	smoke:function(){
		console.log("i am smoking")
		this.health--;
		console.log(this.health)
	},
	drink: function(){
		console.log("i am drining")
		this.health++;
	
	}
}

這裏創建了一個叫test的對象,裏面裝了叫小花的人名,性別,性別。
定義了兩個函數體,分別用來控制小花的健康,抽菸呢健康就在100的基礎上-1,喝酒呢就加一。

我們在控制檯查看
在這裏插入圖片描述

對象的增刪查改

在上面的代碼上進行

”增加“
在這裏插入圖片描述
"刪除"
一定要用delete

在這裏插入圖片描述

"查找"
在這裏插入圖片描述
”改”
也就是在原有基礎上給變量賦值;
在這裏插入圖片描述

構造函數

構造函數 ,是一種特殊的方法。主要用來在創建對象時初始化對象。 即爲對象變量賦初始值。每個構造函數的實例都將共享構造函數的初始值。 構造函數的出現是爲了解決使用Object構造函數和字面量表示法不方便創建大量重複對象的問題。

構造函數的內部原理

  • 在函數體前面隱式的加上this = {};

  • 執行this.xxx = xxx ;

  • 隱式返回this

構造函數的定義的格式:

 修飾符  函數名(形式參數){
        函數體...
    }
  • 構造函數 是沒有返回值類型的。
  • 構造函數的函數名必須要與類名一致
  • 構造函數需要以一個大寫字母開頭,而非構造函數應該以一個小寫字母開頭,這個主要是爲了區別構造函數和其它函數
  • 構造函數其實本身也是函數,只是用來創建對象
  • 調用構造函數千萬不要忘記寫new
  • 如果一個類沒有顯式的寫上一個構造方法時,那麼java編譯器會爲該類添加一個無參的構造函數的。

任何函數,只要通過 new 操作符來調用,那它就可以作爲構造函數 ;

function Student(name) {
    this.name = name; //只在被實例化後的實例中可調用

    this.hello = function () {
        alert('Hello, ' + this.name + '!');
    }
}
var xiaoming = new Student('小明');//this指向小明
xiaoming.name; // '小明'
xiaoming.hello(); // Hello, 小明!

在這裏插入圖片描述

普通函數與構造函數的區別“

1. 返回值類型的區別:

    1. 構造函數是沒有返回值類型 的,
    2. 普通函數是有返回值類型的,即使函數沒有返回值,返回值類型也要寫上void。

2. 函數名的區別:

 1. 構造函數的函數名必須要與類名一致,
  2. 普通函數的函數名只要符合標識符的命名規則即可。

3. 調用方式的區別:

 1. 構造函數是 在創建對象的時候由jvm調用的。
 2. 普通函數是由我們使用對象調用的,一個對象可以對象多次普通 的函數,

4. 作用上的區別:

  1. 構造函數 的作用用於初始化一個對象。
  2. 普通函數是用於描述一類事物的公共行爲的。

包裝類

原始值沒有屬性和方法,但是如果強行添加屬性或者訪問屬性的話,系統就會新建一個包裝類,然後在包裝類上進行操作,操作完成後進行銷燬

JS的數據類型:

**值類型(基本類型)**字符串(String)、數字(Number)、布爾(Boolean)、對空(Null)、未定義(Undefined)、Symbol。

引用數據類型對象(Object)、數組(Array)、函數(Function)。

JS中只有對象纔有屬性和方法,原始值沒有屬性和方法
給基本類型添加屬性和方法:

//給字符串添加方法  要寫到對應的包裝對象的原型下才行
  var str= "hello world";
  //若我們想在原型上設置一個屬性long 保存字符串的長度
  //var str = new String("hello world");// 1.找到基本包裝對象,創建一個和字符串值相同的對象,
  //String.prototype.long=str.length; // 2.通過這個對象找到了包裝對象下的方法並調用 
  //str=null; //  3.這個剛創建的對象被銷燬
  String.prototype.long=str.length;//  執行到這一句,同樣因爲沒有length屬性 後臺會偷偷的執行上述三步操作
  console.log(str.long); //結果爲:11
var str= "hello world";
var str2="我的長度也爲11嗎?";
  String.prototype.long=str.length;
  console.log(str2.long);   //結果爲:11

這樣因爲你是給基本類型string的原型上添加的屬性,所以任意一個字符串都可以訪問到這個屬性及此值。(所以不提倡這種做法)

原始值沒自定義有屬性和方法 不能給原始值賦值(賦值也不會報錯)
js中提供了三種特殊的引用類型(String Number Boolean)每當我們給原始值賦屬性值時 後臺自動調用包裝類轉換
String 默認有length屬性而且不可賦值

var str = 'abc';
str+=1; //str = 'abc1';
var test = typeof str;//test = 'string';
if(test.length == 6){
// var obj = new String(;'abcd1');
// obj.sing = true;
// delete obj;
test.sign= true;
}
// var obj = new String(;'abcd1');
// console.log(obj.sing);
// delete obj;
console.log(test.sign);// undefined

在這裏插入圖片描述

test是字符串string test.length == 6 就是對的, test.sign會把test調用,然後test.sign輸出true???
但是原始值是不能操作屬性的,原始值賦屬性值要調用包裝類

test是原始值,不會有length屬性
代碼執行在test.sign處會new string(test).sign=true
進行包裝類之後就會刪除銷燬,所以到最後一句系統看test原始值沒有sign屬性,又會包裝一遍new string(test).sign。

但這次操作跟上次操作不一樣,系統執行語句進行包裝類,執行完包裝類之後就會銷燬,在執行語句又會在創建包裝類,那麼你的這個新的包裝類中並沒有sign的屬性。

因爲進行了包裝類已經將test變成一個構造對象了,打印對象中的屬性,即使我並沒有這個屬性,系統一樣不會報錯,只會提示undefined。
所以輸出undefied

var x =1,y=z=0;
function add(n){
return n =n+1;
}
y=add(x);
function add(n){
return n = n+3;
}
z=add(x);

xyz分別輸出
輸出1,4,4,
這裏存在一個函數提升,第二個add(n)覆蓋第一個add(n)

原型

  • 原型是function對象的一個屬性,它定義了構造函數製造出的對象的公共先祖。
  • 通過該構造函數產生的對象,可以繼承該原型的方法。
  • 原型也是對象

原型的應用

提取共有屬性,避免代碼冗餘
例如

function Car(color,owner){
  this.color = color;
  this.owner = owner;
  this.carName = "bmw";
  this.height = 1400;
  this.lang = 4900;
  
}
var car = new Car("red","bmw",1400,4900,"prof.ji");
var car1 = new Car("blue","bmw",1400,4900,"long");

在這段代碼中car和car1和carName屬性值都是相同的,我們可以把這些屬性賦給函數原型

Person.prototype = {
  height:1200;
  lang:4900;
  carName : "bmw";
}
function Car(color,owner){
  this.owner = owner;
  this.color = color;
}
var car = new Car("red","prof.ji");
var car1 = new Car("blue","long");

對象修改原型的屬性時只能通過調用原型的屬性進行改變

Person.prototype.lastname = "deng";
function Person(name){
  this.name = name;
}
var person = new Person("sumong");
Person.prototype.lastname = "liu";  //改變原型的屬性

原型的幾個屬性:

prototype屬性:我們只要創建了一個函數,就會根據一組特定的規則爲這個函數創建一個prototype屬性。這個屬性指向函數的原型對象。

constructor屬性:創建了一個自定義構造函數後,其原型對象默認只會取得constructor屬性。指向了構造函數。

_proto_屬性:(這是一種隱式命名規則,是系統命名的): 這個屬性指向了構造方法的原型對象。

1.prototype

Person.prototype.name="xiaohua";
function Person(){
}
var person =new Person();
var person1=new Person();

在這裏原型是Person.prototype
祖先是Person.prototype={ }
在這裏插入圖片描述

  • new Person()創建多個對象person、person1,則多個對象都會同時指向Person構造函數的原型對象。
  • prototype在構造函數出生的時候就已經被定義好了
  • 如果我們訪問person中的一個屬性name,如果在person對象中找到,則直接返回。如果person對象中沒有找到,則直接去person對象的__proto__屬性指向的原型對象中查找
  • 原型相當於函數產生對象的隱形父級,是可以訪問的

2.constructor查看對象的構造函數

任何一個prototype對象都有一個constructor屬性,指向它的構造函數。每一個實例也有一個constructor屬性,默認調用prototype對象的constructor屬性。

function Person(){
}

Car.prototype.say = "def";
function Car(){

}
//Car.prototype = {
//    constructor : Person
//}
var car = new Car();
console.log(car.constructor);//function Car(){}

//console.log(car.constructor);//function Person(){}

//註釋掉的部分:可以更改對象car的構造函數,
//            它本來是function Car(){}實例化的一個對象,
//            現在Car.prototype = {constructor : Person}讓它指向構造函數function Person(){}

3._proto_,查看原型

new一個構造函數,相當於實例化一個對象,這期間其實進行了這三個步驟:

  • 在構造函數的邏輯最頂端隱式的新建一個this對象,this其實不是不是一個 空對象,
    var this = { proto : Person.prototype}//__proto__屬性 指向的是對象原型。 (每個對象都有__proto__屬性,該屬性指向一個對象,就是構造函數Person的原型對象(Person.prototype))

  • 去調用構造函數Person,從而設置對象的屬性和方法並初始化。並返回this對象。(把this返回,這樣每一個實例化的對象就有__proto__屬性了。)

  • 上面步驟完成後,這個對象就與構造函數Person再無聯繫,這個時候即使構造函數Person再加任何成員,都不再影響已經實例化了的對象了。(此時該對象有
    了自己的屬性之後,同時具有了構造函數Person的原型對象的所有成員)

a.用法:

Person.prototype.name = 'abc';
function Person(){
    //var this = {
    //  __proto__ : Person.prototype//指向的是對象原型
   // }
}
var obj ={
    name : "sunny"
}   
var person = new Person();
person.__proto__ = obj;
console.log(person.name);

在這裏插入圖片描述

當new一個對象的時候,開始查找屬性name,
先看自己的構造函數裏面有沒有name屬性,如果有就直接用,
如果沒有就沿着this裏面__proto__ 屬性去對象的原型裏面查找,這個時候我改變了person.proto = obj;讓它指向對象obj,值就是sunny

b.’.'的寫法改變原型對象屬性的值,那麼結果也會跟着改

Person.prototype.name = 'sunny';

function Person(){
//var this = {__proto__ : Person.prototype}
}

// Person.prototype.name = 'cherry';

var person = new Person();

Person.prototype.name = 'cherry';

console.log(person.name)

無論Person.prototype.name = 'cherry’放在var person = new Person();上面還是下面,輸出都是cherry,因爲它改變的是__proto__ 指向的Person.prototype上的值。值都變了,person.name必然會變化。

c.改變原型讓它指向另外的一個空間

var obj = {name : "a"};
var obj1 = obj;//obj和obj1先指向同一個房間,
obj = {name : "b"};//obj它又指向另外一個房間
console.log(obj1.name);//a
console.log(obj.name);//b

Person.prototype.name = 'sunny';
function Person(){
//var this = {__proto__ : Person.prototype}
}
var person = new Person();
Person.prototype = {          //這是把原型給改了,換了一個新對象
    name : 'cherry'
}
console.log(person.name)//sunny

首先new一個對象person,調用構造函數,裏面隱式的var this = {proto : Person.prototype}
讓__proto__ 和 Person.prototype指向同一個空間,然後返回this,這個對象就構建完了。
然後Person.prototype = { name : ‘cherry’ }把自己的空間換了,但是 proto 沒有換,__proto__它還是指向原來的Person.prototype的空間值就是sunny。

那麼查找name屬性的時候,去__proto__裏面找到Person.prototype.name ,結果就是sunny.。
就像下面的過程:

 Person.prototype = {name : "sunny"};
 _proto_ = Person.prototype;
 Person.prototype = {name : "cherry"};
 console.log(_proto_.name);//sunny

Person.prototype.name = 'sunny';
function Person(){
//var this = {__proto__ : Person.prototype}
}
Person.prototype = {         
    name : 'cherry'
}
var person = new Person();
console.log(person.name)//cherry

這個就是Person.prototype = {name : ‘cherry’ }改變了原型對象思維指向,讓它的值爲cherry,然後再實例化一個對象的時候再調用都早函數的時候就會查找__proto__的Person.prototype爲cherry。

d,.改變原型和改變原型屬性的值綜合起來:

Person.prototype.name = 'sunny';
function Person(){
//var this = {__proto__ : Person.prototype}
}
Person.prototype = {  //改變原型    
    name : 'cherry'
}
Person.prototype.name = 'sunny';//改變原型屬性的值
var person = new Person();
console.log(person.name)//sunny

在這裏插入圖片描述

首先是__proto__和 Person.prototype一起指向一個空間值是sunny
然後Person.prototype指向另外一個空間值是cherry,
然後再把第二次Person.prototype指向的空間的內容替換爲sunny,最後實例化對象的時候,發現自己沒有這個name屬性,
根據__proto__ 去原型裏面查找,找到的就是第二次指向的空間裏裏面的值sunny。

1.每當代碼讀取某個對象的某個屬性的時候,都會執行一次搜索。首先從對象實例本身開始,如果在實例中找到了該屬性,則返回該屬性的值,
2.如果沒有找到,則順着原型鏈指針向上,到原型對象中去找,如果如果找到就返回該屬性值。
3.如果爲對象實例添加了一個屬性,與原型中屬性同名,該屬性會屏蔽掉原型中的同名屬性
4.使用delete可以刪除自己實例中的屬性,但是原型中的屬性是刪除不了的。

原型鏈

可以把對象串聯起來的鏈式結構

function CreateDog(name,color){
	this.name = name;
	this.color = color;
}

CreateDog.prototype.say = function(){
	console.log(this.color + this.name + '在叫!');
};

var dog  = new CreateDog('薩摩耶','白色');
dog.say();

訪問原型的方法

1.通過構造函數來訪問
console.log(CreateDog.prototype);//Object { say: say(), … }
2. 通過實例化的對象來訪問
console.log(dog.proto);//Object { say: say(), … }

js 在創建對象(任何對象,普通對象和函數對象)的時候,都有一個__proto__的屬性,
這個屬性用於指向創建他的函數對象的原型對象prototype
console.log(dog.proto === CreateDog.prototype);//true

同樣的,CreateDog.prototype 對象也有一個__proto__ 指向創建他的函數的原型對象 (object)的prototype

console.log(CreateDog.prototype.proto === Object.prototype);//true

Object.prototype 也有一個__proto__ 指向null

console.log(Object.prototype.proto === null);//true

原型鏈 特點是:__proto__ 屬性

Object.prototype是原型鏈的終端

字面量創建對象的方法其實和new Object()的方法是一模一樣的。

var obj = {};
var obj1 = new Object();

它們是相等的。

obj.__proto__ ---->Object.prototype
obj1.__proto__ ---->Object.prototype
Person.prototype = {} ------->Object.prototype
function Person(){
}

函數的原型就是一個字面量的形式,所以原型鏈的終端就是Object.prototype

利用Object.create(原型)創建一個對象*

//var obj = Object.create(原型)
 Person.prototype.name = "sunny";
 function Person() {

 }
 var person = Object.create(Person.prototype);
 console.log(person.name);//sunny

在這裏插入圖片描述
絕大多數對象的最終都會繼承於Object.prototype,但是也有不繼承的情況

Object.create(原型)裏面的"原型"必須是一個Object對象或者空值。(放原始值會報錯)

var obj = Object.create(null);
var obj1 = Object.create(123);//報錯

現在創建一個對象,把null放進去,發現這個對象什麼屬性都沒有了,沒原型了。
在這裏插入圖片描述
調用toString()不行,它自己沒有toString方法,它也沒原型連__proto__屬性都沒有,所以根本找不到toString方法,就會報錯。

人爲加上_proto_屬性,系統不認可
在這裏插入圖片描述

所以說是絕大多數對象繼承於Object.prototype,不是所有的對象,因爲現在的這個Object.create(null)創建的對象根本沒有繼承屬性,連原型都沒有。

toString()方法
  • undefined和null不能調用tostring方法。
  var num = 123;
    num.toString()
  • 可以調用tostring()方法,因爲數字可以通過包裝類來一層層訪問,包裝類肯定是對象,然後對象的原型鏈的終端是Object,它有tostring方法。

  • 但是undefined和null也不是對象,也沒有包裝類,他就是原始值,沒有原型,不可能可以調用tostring()方法。

數字num調用toString()方法的原理:

var num = 123;
//num.toString();------->new Number(num).toString();
//number重寫toString
Number.prototype.toString = function() {}
//Number.prototype.__proto__ = Object.prototype
//Object.prototype.toString = function () {}

首先Object.prototype上有一個toString方法,每一個繼承Object.prototype的都可以調用,但是他們自己也重寫了這個方法,就是Number.prototype.toString = function() {},每次調用toString()就是調用的自己重寫的toString()方法,方便打印出自己想要的結果。

document.write()本質上是調用tostirng方法

var obj = Object.create(null);
document.write(obj);//會報錯
document.write(obj.toString());//會報錯,因爲它沒有原型,更不會有toString()方法
-------------------------------------------------------
//現在人爲的加上toString()方法
var obj = Object.create(null);
obj.toString = function() {
return '訪問';
}
document.write(obj);//訪問
document.write(obj.toString());//訪問
//證明document.write()本質上是調用tostirng方法

在這裏插入圖片描述

call/apply

call() 會改變方法內部this的指向,指向第一個參數,後面的參數是正常傳實參。
apply()第一個參數同樣是指向的對象,但實參只能傳一個數組形式的。
即:

  • call需要把實參按照行形參的個數傳進去。

  • apply需要把實參放進一個arguments傳進去。

  • 兩個方法的第一個參數必須是對象本身

/*call()方法*/
function.call(thisObj[, arg1[, arg2[, [,...argN]]]]);

call:調用一個對象的一個方法,用另一個對象替換當前對象。例如:B.call(A, args1,args2);即A對象調用B對象的方法

/*apply()方法*/
function.apply(thisObj[, argArray])

apply:調用一個對象的一個方法,用另一個對象替換當前對象。例如:B.apply(A, arguments);即A對象應用B對象的方法。

  • 從定義中可以看出,call和apply都是調用一個對象的一個方法,用另一個對象替換當前對象。
  • 而不同之處在於傳遞的參數,apply最多隻能有兩個參數——新this對象和一個數組argArray,如果arg不是數組則會報錯TypeError;
  • apply以數組傳遞參數,call獨立傳遞。
  • call則可以傳遞多個參數,第一個參數和apply一樣,是用來替換的對象,後邊是參數列表。 基本語法:
function myFunction(a, b) {
    return a * b;
}
myObject = myFunction.call(myObject, 10, 2);     // 返回 20
function myFunction(a, b) {
    return a * b;
}
myArray = [10, 2];
myObject = myFunction.apply(myObject, myArray);  // 返回 20

在 JavaScript 嚴格模式(strict mode)下, 在調用函數時第一個參數會成爲 this 的值, 即使該參數不是一個對象。

在 JavaScript 非嚴格模式(non-strict mode)下, 如果第一個參數的值是 null 或 undefined, 它將使用全局對象替代。
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章