都是綁定在使用構造函數建立出來的對象p上; 最終使用的時候也是使用對象p來進行訪問;javascript
function Person(name, age, doFunc) { this.name = name; this.age = age; this.doFunc = doFunc; } var p1 = new Person('sz', 18, function () { console.log('sz在上課'); }); var p2 = new Person('王二小', 18, function () { console.log('王二小在放羊'); });
函數本質也是一個對象, 既然是個對象, 那麼就能夠動態的添加屬性和方法java
只要函數存在, 那麼綁定在它身上的屬性和方法, 也會一直存在編程
eg,記錄總共建立了多少我的對象:segmentfault
// 1. 設置一個全局的變量 var personCount = 0; function Person(name, age, doFunc) { this.name = name; this.age = age; this.doFunc = doFunc; personCount++; } var p1 = new Person('sz', 18, function () { console.log('sz在上課'); }); var p2 = new Person('王二小', 18, function () { console.log('王二小在放羊'); }); console.log('總共建立了'+ personCount + '我的'); //2
function Person(name, age, doFunc) { this.name = name; this.age = age; this.doFunc = doFunc; if (!Person.personCount) { Person.personCount = 0; //建立靜態屬性 } Person.personCount++; } //建立靜態方法 Person.printPersonCount = function () { console.log('總共建立了'+ Person.personCount + '我的'); }; var p1 = new Person('sz', 18, function () { console.log('sz在上課'); }); var p2 = new Person('王二小', 18, function () { console.log('王二小在放羊'); }); Person.printPersonCount();
內置對象
• String
• Number
• Boolean
• Object
• Function
• Array
• Date
• RegExp
• Error數組
獲取根據本身聲明的構造函數建立的對象瀏覽器
console.info("-->str") var str = "aaa"; console.log(typeof str); //string console.log(str.constructor.name); //String console.log(Object.prototype.toString.call(str)); //[object String] console.info("-->obj") var obj = { 'name': '張三' }; console.log(typeof obj); //object console.log(obj.toString()); //[object Object] console.log(obj.constructor.name); //Object console.log(Object.prototype.toString.call(obj)); //[object Object] console.info("-->arr") var arr = [1, 2, 3]; console.log(typeof arr); //object console.log(arr.toString()); //1,2,3 console.log(arr.constructor.name); //Array console.log(Object.prototype.toString.call(arr)); //[object Array] console.info("-->date") var date = new Date(); console.log(typeof date); //object console.log(date.toString()); //Wed Oct 09 2019 00:08:15 GMT+0800 (中國標準時間) console.log(date.constructor.name); //Date console.log(Object.prototype.toString.call(date)); //[object Date]
仍是使用 實例化對象.constructor.name
函數
function Person(name, age) { // var this = new Object(); 自定義類型系統都把this指向Object,因此Object.prototype.toString.call(...) 獲取都是 [object Object] this.name = name; this.age = age; } function Dog(name, age) { this.name = name; this.age = age; } function Cat(name, age) { this.name = name; this.age = age; } // 1. 實例化-->實例 var p = new Person('zs', 18); var d = new Dog('小花', 8); var c = new Cat('小貓', 3); // object console.log(typeof p); console.log(typeof d); console.log(typeof c); // [object Object] console.log(p.toString()); console.log(d.toString()); console.log(c.toString()); // [object Object] console.log(Object.prototype.toString.call(p)); console.log(Object.prototype.toString.call(d)); console.log(Object.prototype.toString.call(c)); console.log(p.constructor.name); //Person console.log(d.constructor.name); //Dog console.log(c.constructor.name); //Cat
類型驗證使用 instanceof
post
function Person(name, age) { this.name = name; this.age = age; } function Dog(name, age) { this.name = name; this.age = age; } function Cat(name, age) { this.name = name; this.age = age; } // 1. 實例化-->實例 var p = new Person('zs', 18); var d = new Dog('小花', 8); var c = new Cat('小貓', 3); //true console.log(p instanceof Person); console.log(d instanceof Dog); console.log(c instanceof Cat); //true console.log(p instanceof Object); console.log(d instanceof Object); console.log(c instanceof Object); //false console.log(p instanceof Dog); console.log(d instanceof Cat); console.log(c instanceof Person); console.log(p); console.log(d); console.log(c); //true console.log([] instanceof Object);
function Person(name, age) { this.name = name; this.age = age; } // 原型對象 Person.prototype.run = function () { console.log('跑'); };
console.log(Person.prototype);
瀏覽器學習
> {run: ƒ, constructor: ƒ} > run: ƒ () > constructor: ƒ Person(name, age) > __proto__: Object
.__proto__看起來很像一個屬性,可是實際上它更像一個getter/setter測試
var p = new Person(); console.log(p.__proto__);
瀏覽器
> {run: ƒ, constructor: ƒ} > run: ƒ () > constructor: ƒ Person(name, age) > __proto__: Object
__proto__
是一個非標準屬性
即ECMAScript中並不包含該屬性,這只是某些瀏覽器爲了方便開發人員開發和調試而提供的一個屬性,不具有通用性
建議:在調試的時候可使用該屬性,但不能出如今正式的代碼中
.__proto__
是可設置屬性,可使用ES6的Object.setPrototypeOf(..)進行設置。然而,一般來講你不須要修改已有對象的[[Prototype]]。
var newYX = { 'add': function () { console.log('sum'); } }; p.__proto__ = newYX; console.log(p.__proto__);
瀏覽器
> {add: ƒ} > add: ƒ () > __proto__: Object
in 判斷一個對象, 是否擁有某個屬性(若是對象身上沒有, 會到原型對象裏面查找)
function Person(name, age) { this.name = name; this.age = age; } Person.prototype.address = '上海'; var p = new Person('撩課', 18); // console.log(name in p); // false console.log('name' in p); // true console.log('address' in p); // true
只到對象自身查找
function Person(name, age) { this.name = name; this.age = age; } Person.prototype.address = '上海'; var p = new Person('張三', 20); console.log(p.hasOwnProperty('name')); // true console.log(p.hasOwnProperty('address')); // false
是的,它們執行相同的操做,都遍歷原型鏈以查找其中的特定對象。
二者的區別在於它們是什麼,以及如何使用它們,例如isPrototypeOf
是對象上可用的函數。它容許您測試一個特定對象是否在另外一個對象的prototype
鏈中,由於此方法是在object
上定義的原型,它對全部對象均可用。
instanceof
是一個操做符,它須要兩個操做數,一個對象和一個構造函數,它將測試傳遞的函數原型屬性是否存在於對象鏈上(經過[[HasInstance]](V)
內部操做,僅在函數對象中可用)。
B.isPrototypeOf(a) 判斷的是A對象是否存在於B對象的原型鏈之中,檢查B
a instanceof B 判斷的是B.prototype是否存在與A的原型鏈之中,檢查B.prototype
isPrototypeOf() 與 instanceof 運算符不一樣。在表達式 "object instanceof AFunction"中,object 的原型鏈是針對 AFunction.prototype 進行檢查的,而不是針對 AFunction 自己。
function A () { this.a = 1; } function B () { this.b = 2; } B.prototype = new A(); B.prototype.constructor = B; function C () { this.c = 3; } C.prototype = new B(); C.prototype.constructor = C; var c = new C(); // instanceof expects a constructor function c instanceof A; // true c instanceof B; // true c instanceof C; // true // isPrototypeOf, can be used on any object A.prototype.isPrototypeOf(c); // true B.prototype.isPrototypeOf(c); // true C.prototype.isPrototypeOf(c); // true
function Person(first, last, age, gender, interests) { this.name = { first, last }; this.age = age; this.gender = gender; this.interests = interests; }; Person.prototype.greeting = function() { console.log('Hi! I\'m ' + this.name.first + '.'); }; function Teacher(first, last, age, gender, interests, subject) { Person.call(this, first, last, age, gender, interests); //用的是傳送給Teacher(),而不是Person()的值 this.subject = subject; } var p = new Person("張","三",22,"男",["籃球","KTV"]); var t = new Teacher("李","四",18,"男",["英語","計算機"],"編程"); t.greeting(); //TypeError: t.greeting is not a function //這裏 Teacher 不能獲取 greeting...
這裏主要調用Person.call(this, first, last, age, gender, interests);
定義 Teacher() 構造器函數,
咱們頗有效的在Teacher()構造函數裏運行了Person()構造函數,獲得了和在Teacher()裏定義的同樣的屬性,可是用的是傳送給Teacher(),而不是Person()的值(咱們簡單使用這裏的this做爲傳給call()的this,意味着this指向Teacher()函數)。
咱們須要讓Teacher()從Person()的原型對象裏繼承方法
Teacher.prototype = Object.create(Person.prototype);
在這個例子裏咱們用這個函數來建立一個和Person.prototype同樣的新的原型屬性值(這個屬性指向一個包括屬性和方法的對象),而後將其做爲Teacher.prototype的屬性值。
看看比較
如今Teacher()的prototype的constructor屬性指向的是Person(), 這是由咱們生成Teacher()的方式決定的。
這或許會成爲很大的問題,因此咱們須要將其正確設置——您能夠回到源代碼,在底下加上這一行代碼來解決:
Teacher.prototype.constructor = Teacher;
注:每個函數對象(Function)都有一個
prototype
屬性,而且只有函數對象有prototype
屬性,由於prototype
自己就是定義在Function
對象下的屬性。當咱們輸入相似var person1=new Person(...)
來構造對象時,JavaScript實際上參考的是Person.prototype
指向的對象來生成person1
。另外一方面,Person()
函數是Person.prototype
的構造函數,也就是說Person===Person.prototype.constructor
(不信的話能夠試試)。在定義新的構造函數
Teacher
時,咱們經過function.call
來調用父類的構造函數,可是這樣沒法自動指定Teacher.prototype
的值,這樣Teacher.prototype
就只能包含在構造函數裏構造的屬性,而沒有方法。所以咱們利用Object.create()
方法將Person.prototype
做爲Teacher.prototype
的原型對象,並改變其構造器指向,使之與Teacher
關聯。任何您想要被繼承的方法都應該定義在構造函數的
prototype
對象裏,而且永遠使用父類的prototype
來創造子類的prototype
,這樣纔不會打亂類繼承結構。
new 與 Object.create 區別
- new的話只能是class(即函數),可是Object.create()的參數能夠爲對象也能夠爲函數,
- 若是Object.create()的參數是對象的話,那麼新的對象會繼承原對象的屬性;若是參數是類(函數)的話,Object.create()的參數爲類(函數)原型,如您所說的,它沒有綁定this,並無屬性被繼承。
- Object.create(null)能夠實現一個空對象,即沒有原型的對象,但使用new就辦不到。
Object.assign(目標對象, ...源對象)
方法用於將全部可枚舉屬性的值從一個或多個源對象複製到目標對象。它將返回目標對象。
//再建立一個基類 function Animal(age) { this.age = age; } Animal.prototype.say = function(language) { console.log('you say ' + language); } function Student(name, sex, age) { Person.call(this, name, sex); Animal.call(this, age); } //原型鏈拼接 Student.prototype = Object.create(Person.prototype); Object.assign(Student.prototype, Animal.prototype); Student.prototype.constructor = Student; Student.prototype.getInfo = function() { console.log('getInfo: [name:' + this.name + ', sex:' + this.sex + ', age:' +this.age + '].'); }; var s = new Student('coco', 'femal', 25);
function Animal(name, age) { this.name = name; this.age = age; } Animal.prototype.eat = function () { console.log('吃'); }; Animal.prototype.run = function () { console.log('跑'); } function Person(name, age, job) { Animal.call(this, name, age); this.job = job; } Person.prototype = Object.create(Animal.prototype); Person.prototype.constructor = Person; //Person.prototype.jump 原型方法必須寫在繼承後 Person.prototype.jump = function () { console.log('跳'); }; function Student(name, age, job, className) { Animal.call(this, name, age); Person.call(this, name, age, job); this.className = className; } Student.prototype = Object.create(Person.prototype); Student.prototype.constructor = Student; //原型方法必須寫在繼承後 Student.prototype.study = function () { console.log('學習'); } function Adolescent(name, age, job, className,sex) { Animal.call(this, name, age); Person.call(this, name, age, job); Person.call(this, name, age, job,className); this.sex = sex; } Adolescent.prototype = Object.create(Student.prototype); Adolescent.prototype.constructor = Adolescent; //原型方法必須寫在繼承後 Student.prototype.play = function () { console.log('跳繩'); } this.a2 = new Adolescent('張同窗',18,'學生','高2.1版',"女"); this.s1 = new Student('張同窗',18,'學生','高2.1版'); this.p1 = new Person('王老師',26,'教師'); this.a1 = new Animal('校長',44);
原型方法必須寫在設置原型 XXX.prototype = Object.create(...)
和設置構造器引用XXX.prototype.constructor = xxx
以後,不容會丟失。
直接給對象動態添加屬性和方法 弊端: 若是操做不少個對象, 則沒法共享 代碼比較冗餘
例子:arr2的run不能執行~
var arr = [1,2,3]; arr.run = function () { console.log('跑'); } arr.run(); var arr2 = [2]; arr.run = function () { console.log('跑'); }
直接給Array原型對象添加方法 弊端: 可能會產生覆蓋的狀況
例子:覆蓋push
Array.prototype.push = function () { console.log('跑'); }; var arr = [1,2,3]; arr.push(); var arr2 = [2]; arr2.push();
提供一個新的構造函數 修改構造函數的原型指向爲數組的原型對象 爲了可以獲取數組裏面的屬性和方法 問題: 依然會修改數組原型對象內容 優化 原型對象就是一個對象 能夠直接根據Array建立一個對象, 給新建立的函數原型對象進行賦值
function MyArray() {} MyArray.prototype = new Array(); MyArray.prototype.run = function () { console.log('跑'); }; var arr = new MyArray(); arr.run();
var obj1 = {name: '張三', age: 18}; var obj2 = {}; for (var key in obj1) { obj2[key] = obj1[key]; } console.log(obj2);
方法用於將全部可枚舉屬性的值從一個或多個源對象複製到目標對象。它將返回目標對象。
var obj3 = {className: '六班'}; Object.assign(obj3, obj1, {address: '重慶'}); console.log(obj3);
深拷貝/淺拷貝
區別:在於對引用值的處理
深拷貝實現
var obj = { name: "唐三三", age: 30, friend: ['張森', '夏贏'], address: { city: '重慶', county: '渝北' }, play: function () { console.log(this.name + '喜歡桌球'); } } //deep Copy function deepCopySource2Target(source, target) { for (var key in source) { var sourceValue = source[key]; if (!(sourceValue instanceof Object)) { //value object target[key] = sourceValue; } else { //object object var temp = new sourceValue.constructor; deepCopySource2Target(sourceValue, temp); target[key] = temp; } } } var newObj = {}; deepCopySource2Target(obj, newObj); console.log(newObj); newObj.play();