一:自定义bind函数
// 第一版 修改this指向,合并参数
Function.prototype.bindFn = function bind(thisArg){
if(typeof this !== 'function'){ // 这里的this指的就是调用bindFn的方法,这里指的就是original方法,如果this不是一个方法那么直接抛出错误
throw new TypeError(this + 'must be a function');
}
// 存储函数本身
var self = this;
// 去除thisArg的其他参数 转成数组,也就是除了替代this的对象之外的参数
var args = [].slice.call(arguments, 1);
var bound = function(){
// bind返回的函数 的参数转成数组
var boundArgs = [].slice.call(arguments);
// apply修改this指向,把两个函数的参数合并传给self函数,并执行self函数,返回执行结果
return self.apply(thisArg, args.concat(boundArgs));
}
return bound;
}
// 测试
var obj = {
name: '轩辕Rowboat',
};
function original(a, b){
console.log(this.name);
console.log([a, b]);
}
var bound = original.bindFn(obj, 1);
bound(2); // '轩辕Rowboat', [1, 2]
二:new操作符的作用
- 创建了一个全新的对象。
- 这个对象会被执行
[[Prototype]]
(也就是__proto__
)链接。 - 生成的新对象会绑定到函数调用的
this
。 - 通过
new
创建的每个对象将最终被[[Prototype]]
链接到这个函数的prototype
对象上。 - 如果函数没有返回对象类型
Object
(包含Functoin
,Array
,Date
,RegExg
,Error
),那么new
表达式中的函数调用会自动返回这个新的对象。
/**
* 模拟实现 new 操作符
* @param {Function} ctor [构造函数]
* @return {Object|Function|Regex|Date|Error} [返回结果]
*/
function newOperator(ctor){
if(typeof ctor !== 'function'){
throw 'newOperator function the first param must be a function';
}
// ES6 new.target 是指向构造函数
newOperator.target = ctor;
// 1.创建一个全新的对象,
// 2.并且执行[[Prototype]]链接
// 4.通过`new`创建的每个对象将最终被`[[Prototype]]`链接到这个函数的`prototype`对象上。
var newObj = Object.create(ctor.prototype);
// ES5 arguments转成数组 当然也可以用ES6 [...arguments], Aarry.from(arguments);
// 除去ctor构造函数的其余参数,想传参数也只能这样传newOperator(ctor, xx, yy)
var argsArr = [].slice.call(arguments, 1);
// 3.生成的新对象会绑定到函数调用的`this`。
// 获取到ctor函数返回结果
var ctorReturnResult = ctor.apply(newObj, argsArr);
// 小结4 中这些类型中合并起来只有Object和Function两种类型 typeof null 也是'object'所以要不等于null,排除null
var isObject = typeof ctorReturnResult === 'object' && ctorReturnResult !== null;
var isFunction = typeof ctorReturnResult === 'function';
if(isObject || isFunction){
return ctorReturnResult;
}
// 5.如果函数没有返回对象类型`Object`(包含`Functoin`, `Array`, `Date`, `RegExg`, `Error`),那么`new`表达式中的函数调用会自动返回这个新的对象。
return newObj;
}
3:作业队列中的执行顺序
在Job queue中的队列分为两种类型:macro-task和microTask。我们举例来看执行顺序的规定,我们设
macro-task队列包含任务:_**a1,a2,a3**_
微任务队列包含任务:_**b1,b2,b3**_
执行顺序为,首先执行marco-task队列开头的任务,也就是_**a1**_任务,执行完毕后,在执行微任务队列里的所有任务,也就是依次执行*** b1,b2,b3 ***,执行完后清空微任务中的任务,接着执行马尔科任务中的第二个任务,依次循环。
了解完完宏任务和微任务两种队列的执行顺序之后,我们接着来看,真实场景下这两种类型的队列里真正包含的任务(我们以节点V8引擎为例),在节点V8中,这两种类型的真实任务顺序如下所示:
宏观任务队列真实包含任务:脚本(主程序代码),setTimeout,setInterval,setImmediate,I / O,UI呈现
micro-task队列真实包含任务:process.nextTick,Promises,Object.observe,MutationObserver
由此我们得到的执行顺序应该为:
script(主程序代码) - > process.nextTick-> Promises ...--> setTimeout - > setInterval - > setImmediate - > I / O - > UI渲染
在ES6中的宏观任务队列又称为ScriptJobs,而微任务又称PromiseJobs
4:构造函数,原型和原型链
构造函数:constructor返回创建实例对象时构造函数的引用。此属性的值是对函数本身的引用,而不是一个包含函数名称的字符串。例:function Parent(age){this.age = age;} var p = new Parent(50); p.constructor === Parent // true,此处的相等告诉我们属性值就是对函数本身的引用。
构造函数本身就是一个函数,与普通函数没有任何区别,构造函数与普通函数的区别在于,使用new生成实例的函数就是构造函数,直接调用就是普通函数
普通函数创建的实例一般没有constructor属性,但是当普通函数具有有效返回值的时候,也会有constructor,但是此时的constructor其实是返回值的constructor
原型:prototype,JavaScript是一种基于原型的语言,这个和Java等基于类的语言不一样,但是有很多相通之处,所以一定要多想想Java是怎么运行的。每个对象拥有一个原型对象,对象以其原型为模板,从原型继承方法和属性,这些属性和方法定义在对象的构造函数的prototype属性上,而非对象实例本身。
循环引用:构造函数Parent的prototype为原型(Parent.prototype),原型的constructor为构造函数
_proto_:_proto_是实例的属性,prototype是构造函数的属性,但是两个属性指向相同
原型链:每个对象拥有一个原型对象,通过_proto_指针指向上一个原型,并从中继承方法和属性,同时原型对象也可能拥有原型,这样一层一层最终指向null,这就是原型链
5,JS继承
继承需要继承父函数的属性和方法,还需要继承父函数原型链的属性和方法
// 定义一个动物类
function Animal (name) {
// 属性
this.name = name || 'Animal';
// 实例方法
this.sleep = function(){
console.log(this.name + '正在睡觉!');
}
}
// 原型方法
Animal.prototype.eat = function(food) {
console.log(this.name + '正在吃:' + food);
};
function Cat(name){
Animal.call(this);
this.name = name || 'Tom';
}
(function(){
// 创建一个没有实例方法的类
var Super = function(){};
Super.prototype = Animal.prototype;
//将实例作为子类的原型
Cat.prototype = new Super();
})();
// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); //true
// 实现继承的函数
function (fn1, fn2) {
}