九、JS中常见的面试题(2) this、工厂方法、构造函数
一、this
解析器在调用函数时每次都会向函数内部传递进一个隐含的参数this,this指向的是函数执行上下文对象,根据函数调用的方式不同this会指向不同的对象。
1、以函数形式调用this永远是window,
2、以方法的形式调用,this是该方法的对象
3、以构造函数形式调用new,this是新创建的实例(使用构造函数创建的对象,称为该类的实例)
二、工厂方法
使用工厂方法创建的对象,使用的构造函数都是Object.,多所以创建的对象都是object这个类型,导致我们无法区分多种不同类型的对象。
三、构造函数
函数:直接调用的Number()、this指向window
构造函数:使用new运算符调用的函数,是构造函数constructor,专门用来创建对象的函数。构造函数就是一个普通的函数,创建方式和普通函数一样,不同的是构造函数习惯上首字母大写。与普通函数的区别是调用方式的区别,
构造函数执行流程:
1、立刻创建一个新的对象
2、将新建对象设置为函数中this,在构造函数中可以使用this来引用新建的对象。
3、逐行执行函数中的代码
4、将新建的对象作为返回值返回
构造函数创建对象,使用同一个构造函数(又称为类)创建的对象,我们称为一类对象,通过构造函数创建的对象,称为该类的实例。instanceof运算符检查一个对象是否是一个类的实例,如果是则返回true,所有的对象都是object的后代用,任何对象用inatanceof object返回都是true
在Person构造函数中,为每一个对象都添加了一个sayName方法,此时这个方法是在构造函数内部创建的,也就是构造函数每执行一次就会创建一个新的sayName方法,(所有该类的实例都有sayName方法,但它们不相等)。也就是所有实例的sayName方法是唯一的,这样就导致构造函数执行一次就会创建一个新的方法,这是没必要的,可以使所有的对象共享一个方法。
将sayName方法(函数)定义到全局作用域中,就会发现此时两个实例的sayName方法相等,他们都是同一个函数。但将函数定义到全局作用域中污染了全局作用域的命名空间,而且定义在全局作用域中不安全(项目不是一个人开发,别人取得名字可能跟你的一样在项目整合时可能就会覆盖你写的方法,所有我们应该使用原型对象上添加方法 ),
2)原型对象prototype
我们所创建的每一个函数(普通、构造),解析器都会向函数中添加一个属性prototype,每个函数的Prototype都是唯一的,不一样的。prototype对应的对象就是原型对象。
如果函数作为普通函数调用Prototype没有任何作用,当函数以构造函数形式调用new时,所创建的对象中都会有一个隐含的属性,指向该构造函数的原型对象,可以通过__proto__(浏览器提供的)来访问该属性。
原型对象就相当于一个公共的区域,所有同一个类的实例都可以访问到这个原型,我们可以将对象中共有的内容,统一设置到原型对象中。当访问一个对象的属性或方法时,会先在自身中寻找,如果有则直接使用;如果没有则去原型中找,
1)直接向mc中添加一个属性a,
2)通过mc的原型对象prototype添加属性a
所有实例都可以访问到,并且不会污染全局作用域,将这些对象共有的属性和方法统一添加到构造函数的原型对象中。
使用in检查对象中是否具有某个属性,如果对象中没有但是原型中有则返回true。如果想要查看对象自己是否具有某个属性则使用hasOwnProperty
原型对象也是对象,也有原型,当使用一个对象的属性或方法时,会先在自身中寻找,自身中有则直接使用;如果没有则去原型中寻找,有则使用,如果没有则去原型对象的原型中寻找,直到找到Object对象的原型,Object是所有对象的祖先,Object对象没有原型,如果在Object对象中没有找到则返回Undefined。
__proto__:在一般浏览器中有2层到第三次就是Null,
当直接在页面中打印一个对象时,实际上 是输出的对象的toString()方法的返回值。如果希望输出对象时 不打印[object,obect]可以为对象添加一个toString()方法