function Parent(name,age,sex){
this.name = name;
this.age = age;
this.sex = sex;
}
var a = new Parent('ljy',18,'男');
console.log(a); //Parent { name: 'ljy', age: 18, sex: '男' }
/*
* 咱們想一下
* 1.在Parent函數中,其實並無返回值,爲何打印中有返回值
* 2.爲何就生成了一個對象
* */
// 本身實現
function _new(prototypeFn,name,age,sex){
// 首先將arguments 轉化爲 數組
var arr = Array.prototype.slice.call(arguments);
var prototypeFn = arr.shift();
var obj = {};
obj.__proto__ = prototypeFn.prototype;
var returnText = prototypeFn.apply(obj,arr);
// 另外:若是構造函數一個新對象,則返回這個新對象,不然返回obj
// 若是返回number string boolean等,new方法會忽略該返回值,返回obj
// 若是構造函數沒有返回,則默認返回obj
if(typeof returnText === 'object' && returnText != null){
return returnText
}else{
return obj;
}
}
var b= _new(Parent,'ljy',18,'男');
console.log(b); //Parent { name: 'ljy', age: 18, sex: '男' }複製代碼
總結:new幹了什麼?ajax
建立一個空對象,做爲將要返回的對象實例。
將這個空對象的原型,指向構造函數的prototype屬性。
將這個空對象賦值給函數內部的this關鍵字。
開始執行構造函數內部的代碼。複製代碼
/*
* 首先說三者的相同點:
* 都是爲了改變函數體內的this指向
*
* 不一樣點:
* call():第一個參數是要this要指向的對象,後面的參數則按照參數順序傳遞進去
* apply():第一個參數是要this要指向的對象,後面的參數以數組格式傳遞進去
* bind():第一個參數是要this要指向的對象,可是其返回的依然是函數
*/
Function.prototype.myCall = function () {
var args = Array.prototype.slice.call(arguments);
// this指向
var context = args.shift();
if(context == null){
context = window
}
context.fn= this;
// 這兒有個問題,若是args是個數組,如[1,2,3],args.toString()會轉爲"1,2,3" 將其當作一個總體,會形成bug
// var result = context.fn(args.toString());
// 解決上述問題,需使用eval方法
if(args.length){
var arr = [];
for(var i= 0; i < args.length; i++){
arr.push('args['+i+']');
}
var result = eval("context.fn(" + arr.toString() + ")");
}else{
var result = context['fn']();
}
delete context.fn;
return result;
}
Function.prototype.myApply = function () {
var args = Array.prototype.slice.call(arguments);
// this指向
var context = args.shift();
// 由於apply函數第2個參數是數組,則若是還有第3個參數,則warn
if(args.length > 1){
throw new Error('apply函數只能有兩個參數');
return false;
}
if(context == null){
context = window
}
context.fn= this;
if(args.length){
var arr = [];
for(var i = 0; i < args[0].length; i++ ){
arr.push('args[0]['+i+']');
}
var result = eval('context.fn('+arr.toString()+')')
}else{
var result = context.fn()
}
delete context.fn;
return result;
}
Function.prototype.myBind = function () {
var args = Array.prototype.slice.call(arguments);
var context = args.shift();
var _this = this;
return function () {
return _this.apply(context,args.concat(Array.prototype.slice.call(arguments)))
}
}
var a = {
name:'ljy',
log:function(x,y){
console.log(this.name);
return this.name+x+y
}
}
var name = 'qianduan'
console.log(a.log.myCall(null,'24','男'))
console.log(a.log.myApply(null,['24','男']))
console.log(a.log.myBind(null)('24','男'))複製代碼
JavaScript具備自動垃圾收集機制。gulp
js引擎是如何執行咱們的代碼的呢?是一行一行逐步執行麼?
數組
// 以最簡單的代碼示意:
console.log('start');
setTimeOut(function(){
console.log('timer')
},0)
console.log('end');
// 打印結果會是 start timer end 嗎?複製代碼
咱們代碼分爲同步異步代碼,常見的異步的代碼 定時器 ajax 事件綁定等等。同步代碼就是咱們常見的變量定義 for if 等等。promise
那js引擎是如何執行代碼的呢?瀏覽器
按照最簡單的解釋,同步代碼會推入執行棧,定時器異步代碼則由定時觸發器線程(這裏注意:js是單線程,但不表明瀏覽器引擎就是單線程,定時器會由定時器線程維護處理。後面會講到瀏覽器的多進程,這裏只要知道ajax啊,定時器啊,都有一個各自的線程維護)觸發,並將其回調函數推入到 「任務隊列」,當執行棧執行結束後,就去任務隊列中尋找,依次執行,這就是所謂的「事件輪詢」(event loop)bash
下面會以詳細的步驟講解上面代碼的執行:babel
總結:執行棧就是在不停的執行同步代碼,異步代碼會被各自的線程維護處理,將各自的回掉函數推入到任務隊列,(因此說任務隊列就是一系列的回掉函數的集合),當執行棧爲空時,就會去任務隊列尋找,依次執行對應的回掉函數。 app
其實咱們發現,上面的廣義的同步異步,上面的解釋足夠了,可是ES6以後,出現了promise等,在標準中,將promise認定爲「微任務」。(微任務是瀏覽器層面的處理,因此咱們使用babel-pollfill這種實際上是沒法模擬微任務的,因此在babel-pollfill中對promise的實現實際上是以宏任務處理的)異步
那宏任務包含什麼,微任務包含什麼?函數
能夠簡單認爲這是剝洋蔥的順序
console.log('1');
setTimeout(function() {
console.log('2');
new Promise(function(resolve) {
console.log('3');
resolve();
}).then(function() {
console.log('4')
})
})
new Promise(function(resolve) {
console.log('5');
resolve();
}).then(function() {
console.log('6')
})
// 上述代碼會如何執行?複製代碼
解釋:
1.當總體的script做爲第一個宏任務進入主線程,會打印出1
2.遇到定時器,又是個宏任務,定時器線程在制定ms後將其回掉函數推入到宏任務任務隊列。
3.遇到微任務Promise,會先打印出5,爲何?由於這一步是同步代碼,真正的微任務是then裏面的回掉函數。(筆者寫過promise的實現過程,詳細點擊https://juejin.im/post/5c27130f6fb9a049ef26a936)
在本輪的事件循環中,總體的script是一個宏任務,看成根任務,裏面有一個宏任務一個微任務,在本輪執行完畢後,會先檢查其子輩中有沒有微任務,若是有,則執行。不然,開啓下一個宏任務。
畫圖示意:
總結:在當前的宏任務中,它老是會先尋找到其內部的微任務執行,而後再執行內部宏任務,內部的宏任務再先尋找其內部的微任務執行,而後再執行其內部宏任務,依次往下。
它接收函數做爲參數或將函數做爲輸出返回。
其實咱們已經使用過不少的高階函數,好比filter reduce,因此不要被概念嚇破膽。
字面意思就很好理解。多個函數的組合唄。好比管道。
function compose(fn1,fn2){
return function(arg){
fn2(fn1(arg));
}
}
// 第一個函數的返回值會傳入到第2個函數的參數裏,執行。是否是很像gulp裏的pipe
複製代碼
上面這些概念不要特別去記,其實咱們一直在使用它們。