new 運算符建立一個用戶定義的對象類型的實例或具備構造函數的內置對象的實例javascript
this
的上下文 ;this
。
- 當構造函數沒有返回值時:
function Animal (name, age) {
this.name = name;
this.age = age;
this.speak = '汪汪'
}
Animal.prototype.color = 'red';
Animal.prototype.say = function () {
console.log('我會叫--' + this.speak);
}
var dog = new Animal('旺財', '3');
console.log(dog.name) // 旺財
// 打印一個沒有的屬性
console.log(dog.eat) // undefined
console.log(dog.age) // 3
console.log(dog.speak) // 汪汪
console.log(dog.color) // red
dog.say(); // 我會叫--汪汪
複製代碼
- 當構造函數有返回值,且返回值類型爲對象的(不包含 null )
有返回對象的,new 實例化後的對象就是返回的對象。拿不到內部其餘的屬性,也不會獲取到原型上的屬性和方法java
function Animal2 (name, age) {
this.name = name;
this.age = age;
this.speak = '汪汪'
return {
name,
eat: '吃骨頭',
}
}
Animal2.prototype.color = 'red';
Animal2.prototype.say = function () {
console.log('我會叫--' + this.speak);
}
var dog = new Animal2('旺財', '3');
console.log(dog.name) // 旺財
console.log(dog.eat) // 吃骨頭
console.log(dog.age) // undefined
console.log(dog.speak) // undefined
console.log(dog.color) // undefined
dog.say(); // 報錯: dog.say is not a function
複製代碼
- 構造函數返回 null,
這種狀況實際上和第一種狀況同樣(至關於構造函數沒有返回值)git
當時確實要注意的是,typeof null === 'object' 全部在實現的時候,咱們要進行處理。不能看到 typeof 返回的是object,那麼就返回該值。具體實例看下面的講解。es6
function Animal3 (name, age) {
this.name = name;
this.age = age;
this.speak = '汪汪'
return null
}
Animal3.prototype.color = 'red';
Animal3.prototype.say = function () {
console.log('我會叫--' + this.speak);
}
var dog = new Animal3('旺財', '3');
console.log(dog.name) // 旺財
console.log(dog.age) // 3
console.log(dog.speak) // 汪汪
console.log(dog.color) // red
dog.say(); // 我會叫--汪汪
複製代碼
function fakeNew() {
var obj = {};
// 獲取第一個參數,而且會刪除第一個參數
var Constructor = [].shift.call(arguments);
// obj 繼承構造函數上的方法
obj.__proto__ = Constructor.prototype;
// Constructor 方法中的 this 指向 obj, 執行 Constructor 方法,至關於給 obj 繼承了Constructor 中的屬性,方法。 (能夠理解就是經過 apply 實現繼承)
var result = Constructor.apply(obj, arguments);
if(typeof result === 'object'){
// result || obj 防止返回的是 null(由於 typeof null == 'object');
return result || obj;
} else {
return obj;
}
};
複製代碼
咱們來簡寫一下 New ,經過 ES6 寫很簡單 3 行代碼搞定。github
function fakeNew(Constructor, ...rest) {
// Object.create 方法建立一個新對象,使用現有的對象來提供新建立的對象的__proto__。
var obj = Object.create(Constructor.prototype),
var result = Constructor.apply(obj, rest);
return typeof result === 'object' ? result || obj : obj;
};
複製代碼
爲何會經過 Object.create 方法來實現讓對象的原型繼承 Constructor.prototype 屬性呢?數組
因爲現代 JavaScript 引擎優化屬性訪問所帶來的特性的關係,更改對象的
[[Prototype]]
在***各個***瀏覽器和 JavaScript 引擎上都是一個很慢的操做。其在更改繼承的性能上的影響是微妙而又普遍的,這不單單限於obj.__proto__ = ...
語句上的時間花費,並且可能會延伸到***任何***代碼,那些能夠訪問***任何***[[Prototype]]
已被更改的對象的代碼。若是你關心性能,你應該避免設置一個對象的[[Prototype]]
。相反,你應該使用Object.create()
來建立帶有你想要的[[Prototype]]
的新對象。瀏覽器
出自 MDN 原話app
也就是說 Object.setPrototypeOf(obj, Constructor.prototype)
和wordpress
var obj = {};obj.__proto__ = Constructor.prototype
兩種方法,不管在寫法仍是性能上都沒有 Object.create
方法來的更好函數
上面模擬實現的 new 方法中
var result = Constructor.apply(obj, rest)
; 這行代碼作了什麼?
其實就是經過 apply 來實現屬性和方法的繼承。不清楚的同窗能夠看下 apply 的模擬實現過程。
Function.prototype.fakeApply = function (obj, arr) {
var context = obj || window;
context.fn = this;
var result;
if (!arr) {
result = context.fn();
} else {
var args = [];
for (var i = 0, len = arr.length; i < len; i++) {
args.push('arr[' + i + ']');
}
result = eval('context.fn(' + args + ')')
}
delete context.fn
return result;
}
複製代碼
咱們用 ES6 寫法來簡寫一下 apply
Function.prototype.fakeApply = function(obj, arr = []) {
var context = obj || window;
context.fn = this;
result = context.fn(...arr);
delete context.fn;
return result
};
複製代碼
同理 call 的模擬方法
// 經過...arr 接收後面全部的參數
Function.prototype.fakeCall = function(obj, ...arr) {
var context = obj || window;
context.fn = this;
// 將接受的參數展開執行
result = context.fn(...arr);
delete context.fn;
return result
};
複製代碼
在開發過程當中根據實際狀況選擇使用方式,那麼均可以用的話建議選擇 call 方法,由於 call 的性能高於 aplly。
由於 apply 第二個參數是數組,最終執行的時候仍是要將數組轉變成一個一個參數,來執行函數,所以性能比 apply 差 ( 也就是差一點點 )
Array.prototype.myMap = function(fn){
const arr = this;
const newArr = [];
for(let i = 0; i<arr.length; i++){
var temp = fn(arr[i], i, arr)
newArr.push(temp);
}
return newArr;
}
複製代碼
完整的 map 他是有第二個參數的
Array.map( callback [, thisArg ] )
callback
原數組中的元素調用該方法後返回一個新數組。它接收三個參數,分別爲 currentValue、index、array。
currentValue
callback的第一個參數,數組中當前被傳遞的元素。
index
callback的第二個參數,數組中當前被傳遞的元素的索引。
array
callback的第三個參數,調用map()方法的數組,即原數組。
thisArg
執行callback函數時this指向的對象
Array.prototype.map polyfill源碼實現:地址傳送門
// 實現 ECMA-262, Edition 5, 15.4.4.18
// 參考: http://es5.github.io/#x15.4.4.18
if (!Array.prototype.map) {
Array.prototype.map = function(callback, thisArg) {
var T, A, k;
if (this == null) {
throw new TypeError(" this is null or not defined");
}
// 1. 將O賦值爲調用map方法的數組.
var O = Object(this);
// 2.將len賦值爲數組O的長度.
var len = O.length >>> 0;
// 3.若是callback不是函數,則拋出TypeError異常.
if (Object.prototype.toString.call(callback) != "[object Function]") {
throw new TypeError(callback + " is not a function");
}
// 4. 若是參數thisArg有值,則將T賦值爲thisArg;不然T爲undefined.
if (thisArg) {
T = thisArg;
}
// 5. 建立新數組A,長度爲原數組O長度len
A = new Array(len);
// 6. 將k賦值爲0
k = 0;
// 7. 當 k < len 時,執行循環.
while(k < len) {
var kValue, mappedValue;
//遍歷O,k爲原數組索引
if (k in O) {
//kValue爲索引k對應的值.
kValue = O[ k ];
// 執行callback,this指向T,參數有三個.分別是kValue:值,k:索引,O:原數組.
mappedValue = callback.call(T, kValue, k, O);
// 返回值添加到新數組A中.
A[ k ] = mappedValue;
}
// k自增1
k++;
}
// 8. 返回新數組A
return A;
};
}
複製代碼
簡易實現
Array.prototype.myReduce = function(callback, initialValue ) {
var previous = initialValue, k = 0, length = this.length;
// 若是沒有傳入 initialValue 就默認將數組的第 0 個賦給 previous
if (typeof initialValue === "undefined") {
previous = this[0];
k = 1;
}
if (typeof callback === "function") {
for (k; k < length; k++) {
this.hasOwnProperty(k) && (previous = callback(previous, this[k], k, this));
}
}
return previous;
};
複製代碼
簡易實現
Array.prototype.MyForEach = function(fn) {
const arr = this
for(let i = 0; i<arr.length; i++){
fn(arr[i],i,arr)
}
}
複製代碼
const deepCopy = function(obj){
return Object.keys(obj).reduce(function (copy, item){
// 若是對象的 value 類型爲 object 就再次執行 deepCopy 來實現深拷貝
copy[item] = typeof obj[item] === 'object' ? deepCopy(obj[item]) : obj[item];
return copy;
// 判斷 obj 是 數組類型 仍是對象類型 這樣 copy 就是 [] 或者是 {}
}, Object.prototype.toString.call(obj) === '[object Array]' ? [] : {})
}
複製代碼
使用箭頭函數和 ,逗號運算符,簡化
const deepCopy = obj => Object.keys(obj).reduce(
(copy, item) => (copy[item] = typeof obj[item] === 'object' ? deepCopy(obj[item]) : obj[item], copy)
,Object.prototype.toString.call(obj) === '[object Array]' ? [] : {}
);
複製代碼
for(var i=1;i<=3;i++)
{
  for(var i=1;i<3;i++)
  {
    console.log(i)
  }
}
// 1 2
for(let i=1;i<=3;i++)
{
  for(let i=1;i<3;i++)
  {
    console.log(i)
  }
}
// 1 2 1 2 1 2
複製代碼
先執行一次再去作判斷
var a = 10
do{
console.log(a--)
} while (a>2)
複製代碼
先判斷, 再執行
while(i<=10)
{
  循環體
  i++
}
複製代碼
var obj = {
a: 1,
b: [],
c: function () {}
};
for (var key in obj) {
console.log(key);
}
// 結果是:
// a
// b
// c
複製代碼
Object.prototype.objCustom = function() {};
Array.prototype.arrCustom = function() {};
var arr = [3, 5, 7];
arr.foo = 'hello';
for (var i in arr) {
console.log(i);
}
// 結果是:
// 0
// 1
// 2
// foo
// arrCustom
// objCustom
複製代碼
自定義的 Array.prototype 上的方法也會遍歷出來,經過原型鏈最後還能遍歷到 arr.proto.proto Object.prototype 上的objCustom
hasOwnProperty() 方法會返回一個布爾值,指示對象自身屬性中是否具備指定的屬性
Object.prototype.objCustom = function() {};
Array.prototype.arrCustom = function() {};
var arr = [3, 5, 7];
arr.foo = 'hello';
for (var i in arr) {
if (arr.hasOwnProperty(i)) {
console.log(i);
}
}
// 結果是:
// 0
// 1
// 2
// foo
複製代碼
數組上面自己的屬性仍是會遍歷出來
若是不想要自己的屬性能夠經過 forEach 來實現
Object.prototype.objCustom = function() {};
Array.prototype.arrCustom = function() {};
var arr = [3, 5, 7];
arr.foo = 'hello';
arr.forEach((item, index)=>{
console.log(item, index)
})
// 結果是:
// 3 0
// 5 1
// 7 2
複製代碼
或者經過 for of 來實現
for of 須要遍歷可迭代(iterable)的類型
var obj = {
a: 1,
b: [],
c: function () {}
};
for (var key of obj) {
console.log(key);
}
// 出錯:
// Uncaught TypeError: obj is not iterable
複製代碼
ES6 標準不認爲 Object 對象是可迭代的,因此不能用 for-of 遍歷之
可迭代的類型有 Array、 Map、Set、arguments 類對象、NodeList 這類 DOM 集合、String**、**generators
全文章,若有錯誤或不嚴謹的地方,請務必給予指正,謝謝!
參考