高级2:this&原型链&继承
问题1: apply、call 、bind有什么作用,什么区别
- apply、 call的作用和区别
基本语法:fn.apply(context, [parama, param2, param3...])
;fn.call(context,param1, param2, param3...)
作用:使函数在运行this
指向context
区别:call方法中传入的是一个参数列表,而apply方法中的参数二是一个数组或类数组对象 - call,apply常用用法:
- 数组之间的累加;
- 获取数组中的最大值和最小值,利用他们扩充作用域拥有Math的min和max方法;由于没有什么对象调用这个方法,所以第一个参数可以写作null或者本身;
var num = [1,4,23,7,8,10]; var maxNum = Math.max.apply(Math,num);//23 var minNum = Math.min.call(Math,1,4,23,7,8,10)//1
- 验证是否是数组(前提是toString()方法没有被重写过)
var a = [1,23,4] var b = 100 function isArray(obj){ return Object.prototype.toString.call(obj) === '[object Array]' ; } isArray(a)//true isArray(b)//false
- 让类数组拥有数组的方法
function joinStr(){ // return arguments.join('-') 类数组没有数组的方法 console.log(Array.prototype.join.call(arguments,'-')) //在数组的原型上与下面等同 a-b-c-d console.log([].join.call(arguments,'-'))//a-b-c-d } joinStr('a','b','c','d')
- bind的作用:返回一个新的函数function,并使function内部的this指向传入的第一个参数,传入bind()方法的第二个及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数;
var obj = {
name: 'Byron',
fn : function(){
console.log(this);
}
}
var obj2 = { name: 'obj2' }
var fn2 = obj.fn.bind(obj2); //this指向obj2 fn2()
- 关于三者:
- 都是用来改变函数的this对象的指向的;
- 第一个参数都是this要指向的对象;
- 都可以利用后续参数传参;
- bind是返回对应函数,便于稍后调用,apply、call是立即调用;
问题2: 以下代码输出什么?
var john = {
firstName: "John"
}
function func() {
alert(this.firstName + ": hi!")
}
john.sayHi = func
john.sayHi()
输出的结果为:弹出John: hi!
对象的方法调用,this指向调用方
问题3: 下面代码输出什么,为什么
func()
function func() {
alert(this)
}
输出结果:弹出window
问题4:下面代码输出什么
document.addEventListener('click', function(e){
console.log(this);
setTimeout(function(){
console.log(this);
}, 200);
}, false);
第一个console.log(this)
中的this
指向DOM,输出的是document
第二个console.log(this)
中的this
指向全局,输出的是window
问题5:下面代码输出什么,why
var john = {
firstName: "John"
}
function func() {
alert( this.firstName )
}
func.call(john)
输出结果:弹出John
,call
调用,this
指向call
的第一个参数,这里是call(john)
指向了john对
象里的this
问题6: 以下代码有什么问题,如何修改
var module= {
bind: function(){
$btn.on('click', function(){
console.log(this) //this指什么
this.showMsg();
})
},
showMsg: function(){
console.log('饥人谷');
}
}
console.log(this)
中的this
指向DOM(此处为绑定事件$btn
),应该让this
指向module
这样当点击按钮是才会打印 ‘饥人谷’
修改:
var module= {
var _this = this
bind: function(){
$btn.on('click', function(){
console.log(this) //this指什么
_this.showMsg();
})
},
showMsg: function(){
console.log('饥人谷');
}
}
问题7:有如下代码,解释Person、 prototype、proto、p、constructor之间的关联。
function Person(name){
this.name = name;
}
Person.prototype.sayName = function(){
console.log('My name is :' + this.name);
}
var p = new Person("若愚")
p.sayName();
- 分析:
Person
是构造函数,也是一个对象,这个对象里面存在一个prototype
属性,而构造函数内部定义了实例的属性和方法,这些属性和方法是属于该类的所有实例的特征;
p是通过构造函数Person
构造出来的实例,也是拥有__proto__
属性;prototype
是构造函数内部的原型对象,所以拥有contructor
和__proto__
属性,其中contructor
属性指向构造函数Person
,__proto__
指向该对象的原型 - 总结:
p.__proto__ === Person.prototype
p.__proto__.constructor === Person
Person.prototype.constructor === Person
Person.prototype.__proto__ === Object.prototype
Object.prototype.constructor === Object
第一句话:
prototype
是函数的原型对象,即prototype
是一个对象,它会被对应的__proto__
引用。
第二句话:要知道自己的__proto__
引用了哪个prototype
,只需要看看是哪个构造函数构造了你,那你的__proto__
就是那个构造函数的prototype
。
第三句话:所有的构造函数的原型链最后都会引用Object
构造函数的原型,即可以理解Object
构造函数的原型是所有原型链的最底层,即Object.prototype.__proto===null
作者:宣泽彬
链接:https://www.jianshu.com/p/7119f0ab67c0
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
问题8: 上例中,对对象 p可以这样调用 p.toString()。toString是哪里来的? 画出原型图?并解释什么是原型链。
- toString()是从原型Object里面继承的
-
原型图
原型链 - 什么是原型链
原型对象本身也是对象,而每个javascript
对象都有一个原型对象,每个对象都有一个隐藏的__proto__
属性,原型对象也有自己的原型,而它自己的原型对象又可以有自己的原型,这样就组成了一条链,这个就是原型链。在访问对象的属性时,如果在对象本身中没有找到,则会去原型链中查找,如果找到,直接返回值,如果整个链都遍历且没有找到属性,则返回undefined
。原型链一般实现为一个链表,这样就可以按照一定的顺序来查找。
问题9:对String做扩展,实现如下方式获取字符串中频率最高的字符
var str = 'ahbbccdeddddfg';
var ch = str.getMostOften();
console.log(ch); //d , 因为d 出现了5次
String.prototype.getMostOften = function (){
var obj ={} //存放每个i出现的次数
var k;
for(var i =0 ; i<this.length ; i++){
k = this[i];
if(obj[k]){
obj[k] ++ //如果存在,对应的值+1;
}else{
obj[k] = 1 //如果obj[k]不存在 给他赋值 =1 ;
}
}
var max = 0;
var key ;
for(var j in obj ){ //循环obj
if(obj[j]>max){
max = obj[j] //如果obj[j]的值大于max 就赋值给max 最后max获取obj[j]里最大的的值;
key = j
}
}
return key;
}
var str = 'ahbbccdeddddfg';
var ch = str.getMostOften();
console.log(ch); //d
运行结果.png
问题10: instanceOf有什么作用?内部逻辑是如何实现的?
instanceOf
是一个运算符。object instanceof constructor
,instanceof
用来检测 constructor.prototype
是否存在于参数 object
的原型链上,如果存在则返回true
;否则false
(简单理解:A instancrof B用于判断A 是否是B 的一个实例,或者是A是否是以B为父类型的一个实例。)
function Person(){}
var p = new Person()
p instanceof Person //true
Person instanceof Object //true
p instanceof Object //true
p.__proto__.__proto__===Object.prototype //ture
内部逻辑:
function isInstanceOf(obj, fn){
var objproto = obj.__proto__;
do{
if(objproto === fn.prototype){
return true;
} else {
objproto = objproto.__proto__;
}
}while(objproto)
return false;
}
问题11:继承有什么作用?
- 概念:继承是指一个对象直接使用另一个对象的属性和方法。
- 作用:
- 继承划分了类的层次性,父类代表的是更一般、更泛化的类,而子类则是更为具体、更为细化;
- 子类拥有父类的属性和方法,不需要重复写代码,修改时也只需修改一份代码.
- 可以重写和扩展父类的属性和代码,又不影响父类本身;
- 优化内存空间
问题12: 下面两种写法有什么区别?
//方法1
function People(name, sex){
this.name = name;
this.sex = sex;
this.printName = function(){
console.log(this.name);
}
}
var p1 = new People('饥人谷', 2)
//方法2
function Person(name, sex){
this.name = name;
this.sex = sex;
}
Person.prototype.printName = function(){
console.log(this.name);
}
var p1 = new Person('若愚', 27);
方法1:将printName()作为构造函数的方法,在实例化的时候,每次都要创建printName(),作为新对象的私有属性,这样较为消耗内存;
方法2:将printName()作为构造函数原型上的方法,在实例化的时候,所有实例共享这个方法,可以优化内存空间
问题13: Object.create 有什么作用?兼容性如何?
- 作用:Object.create() 方法使用指定的原型对象和其属性创建了一个新的对象,如下代码
- 兼容性 :can i use
兼容性.png
问题14: hasOwnProperty有什么作用? 如何使用?
- 作用:obj,hasOwnProperty(pro) 判断某个对象是否包含自定义属性而不是从原型链上继承到的属性。
- 用法:
function People(){
this.name=name
}
People.prototype.sayName=function(){
console.log(this.name)
}
var p = new People()
p.hasOwnProperty('name')//true
p.hasOwnProperty('sayName')//false
hasOwnProperty.png
问题15:如下代码中call的作用是什么?
function Person(name, sex){
this.name = name;
this.sex = sex;
}
function Male(name, sex, age){
Person.call(this, name, sex); //这里的 call 有什么作用
this.age = age;
}
使Male
创建的实例能够继承Person
上的属性。
问题16: 补全代码,实现继承
function Person(name, sex){
// todo ...
}
Person.prototype.getName = function(){
// todo ...
};
function Male(name, sex, age){
//todo ...
}
//todo ...
Male.prototype.getAge = function(){
//todo ...
};
var ruoyu = new Male('若愚', '男', 27);
ruoyu.printName();
function Person(name, sex){
// todo ...
this.name = name
this.sex = sex
}
Person.prototype.getName = function(){
// todo ...
console.log('name:'+ this.name)
};
function Male(name, sex, age){
//todo ...
this.age = age
Person.call(this, name, sex)
}
//todo ...
Male.prototype = Object.create(Person.prototype)
Male.prototype.constructor = Male;
Male.prototype.getAge = function(){
//todo ...
console.log('age:'+ this.age)
};
var ruoyu = new Male('若愚', '男', 27);
ruoyu.printName();