目录:1:prototype(原型对象);2:关于js当中的this;3:js中new函数后带括号和不带括号的区别 4:函数apply和call;5:call
、apply
原生实现解析
一、prototype(原型对象)
如下代码我们知道对于一个一存在的构造器的对象是不能添加新属性的,也就是说Person.nationality = "English";这样是行不通的,要添加一个新的属性需要在在构造器函数中添加。
function Person(first, last, age, eyecolor) { this.firstName = first; this.lastName = last; this.age = age; this.eyeColor = eyecolor; } var myFather = new Person("John", "Doe", 50, "blue"); var myMother = new Person("Sally", "Rally", 48, "green");
1.1、prototype 继承
所有的 JavaScript 对象都会从一个 prototype(原型对象)中继承属性和方法。
-
Date
对象从Date.prototype
继承。 -
Array
对象从Array.prototype
继承。 -
Person
对象从Person.prototype
继承。
JavaScript 对象有一个指向一个原型对象的链。当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。
Date
对象, Array
对象, 以及 Person
对象从 Object.prototype
继承。
如下代码就可以给对象的构造函数添加新的属性:
function Person(first, last, age, eye) { this.firstName = first; this.lastName = last; this.age = age; this.eyeColor = eye; } Person.prototype.nationality = "English";
var myFather = new Person("John", "Doe", 50, "blue");
// myFather.nationality == English
同样也可以添加新的方法如下:
Person.prototype.name = function() { return this.firstName + " " + this.lastName };
// var myFather = new Person("John", "Doe", 50, "blue");
// myFather.name();
二、关于js当中的this
定义:当前执行上下文(global
、function
或 eval
)的一个属性,在非严格模式下,总是指向一个对象,在严格模式下可以是任意值。
接下来我们来看看不同情况下this
的指向。重点:this
的指向是调用时决定的。
2.1)在全局上下文中,this
指向的是window
2.2)函数调用中——直接调用this
指向window
function test(){ console.log(this) //window } test(); function test(){ test1() function test1(){ console.log(this) //window } } test();
2.3)通过对象调用——this
指向调用方法的对象
const obj = { a:1, test:function(){ console.log(this) // this为obj对象 } } console.log(obj.test())
2.4)箭头函数——this
与所在上下文this
指向相同
const fun1 = ()=>{ console.log(this) // window } console.log(fun1()) //window function fn1(){ console.log(this) // window return ()=>{ console.log(this) // window } } console.log(fn1()())
2.5) 构造函数——this
指向构造出的对象
function Test(){ this.name = ‘test‘; // Test对象 } let t = new Test(); console.log(t.name) // test
构造函数配合new使用, 而new关键字会将构造函数中的this指向实例化对象,所以构造函数中的this->实例化对象
new关键字会在内部发生什么
//第一行,创建一个空对象obj。 var obj ={}; //第二行,将这个空对象的__proto__成员指向了构造函数对象的prototype成员对象. obj.__proto__ = CO.prototype; //第三行,将构造函数的作用域赋给新对象,因此CA函数中的this指向新对象obj,然后再调用CO函数。 CO.call(obj); //第四行,返回新对象obj。 return obj;
三、js中函数后带括号和不带括号的区别
function test(){ return 1; } var a=test; console.log(a);//输出[Function: test] var b=test(); console.log(b);//输出1
函数名是一个对象,而对象是保存内存中,函数名是指向这个对象的指针。
var a=test便是这个函数对象的指针给a。
var b=test()后面加上圆括号就表示立即调用,范围函数的返回值。还可以在函数体后面加圆括号表示立即调用。
四、函数 apply和call
apply:方法重用,通过apply方法可以编写用于不同对象的方法。
apply() 和 call() 作用一样,传参数形式不一样
apply()方法传入两个参数,一个是函数上下文的对象,一个是该函数传入的数组形式的参数
call()方法传入两个参数,第一个参数是上下文对象,第二类参数以列表形式传入
1.1、两个方法的作用:
- 改变this指向
- 借用别的对象的方法
- 调用函数
1) 第一个参数传入的Person对象,getName函数中的this便指向了 Person对象。而如果我们直接调用通过getName(‘Tom‘,‘Tim‘);这个时候this指向的是Window对象。
即:getName()的this所指向的上下文对象就为Person
var Person = {name :‘Lucy‘} function getName(name1,name2){ console.log(this.name + name1 + name2); //Lucy Tom Tim } getName.apply(Person,[‘Tom‘,‘Tim‘]); // 第一个参数指向person对象 ,this.name 指向 Person.name ; // 第二个参数是一个数组,数组中的元素分别代表函数的参数一和参数二
2)借用别的对象的方法(用一个对象替换掉另一个对象(this))
如下:Person1.call(this); // 作用:使用 Person1 对象代替 this 对象,此时Person2 就有了 Person1 中的所有属性和方法了
var Person1 = function(){ this.name = ‘LiLi‘; } var Person2 = function(){ Person1.call(this); // 作用:使用 Person1 对象代替 this 对象,此时Person2 就有了 Person1 中的所有属性和方法了
} var person = new Person2();
明白了上面的代码后下面的代码就非常清楚了:
var Person1 = function(){ this.name = ‘LiLi‘; } var Person2 = function(){ this.getName = function() { console.log(this.name); // LiLi } // 实质是使用 Person1 对象代替 this 对象, 那么 Person2 就有了 Person1 中的所有属性和方法了 // 相当于 Person2 继承了 Person1 的属性和方法。 Person1.call(this); } var person = new Person2(); person.getName();
1.2、在数组上模拟 max 方法
Math.max(1,2,3); // 会返回 3
由于 JavaScript 数组没有 max() 方法,因此您可以应用 Math.max() 方法。
Math.max.apply(null, [1,2,3]); // 也会返回 3
在 JavaScript 严格模式下,如果 apply() 方法的第一个参数不是对象,则它将成为被调用函数的所有者(对象)。在“非严格”模式下,它成为全局对象。
五、call、apply原生实现解析
//call函数原生模拟实现 //给context一个默认值,如果没有传入,默认指向window,用...来将传入参数转为数组 Function.prototype.call = function(context = window,...args){ //将调用call的函数挂在传入的对象上,起到更改this的目的 context.fun = this; //执行方法,并传入参数 const result = context.fun(...args); //执行完毕删除刚创建的自定义方法,防止污染 delete context.fun; //返回结果 return result } // 调用 let obj = {a:1} function test(){console.log(this.a)} test.call(obj); // 输出1
//apply函数原生模拟实现 //给context一个默认值,如果没有传入,默认指向window,用...来将传入参数转为数组 Function.prototype.apply = function(content=window,args){ //将调用call的函数挂在传入的对象上,起到更改this的目的 content.fn = this; //执行方法,并传入参数 let result = content.fn(...args); //执行完毕删除刚创建的自定义方法,防止污染 delete content.fn; //返回结果 return result; } let obj = {a:1} function test(){console.log(this.a)} test.apply(obj);