本文共 5762 字,大约阅读时间需要 19 分钟。
Javascript是一种基于原型的面向对象语言,而不是基于类的面向对象语言。JS是基于原型的语言,它只有原型对象的概念。原型对象就是一个模板,新的对象从这个模板构建,从而获得最初的属性,任何对象在运行时可以动态的增加属性。而且任何一个对象都可以作为另一个对象的原型,这样后者就可以共享前者的属性。
let obj = { a:1, b: 'abc', 'c': 100, show: () => { console.log('show function') }}let obj1 = {obj}console.log(obj1)// { obj: { a: 1, b: 'abc', c: 100 } }let x = 100, y = 200, z = 'abc'let obj2 = {x: x, y, z} // 注意第一个x是字符串key,第二个x是变量,y,z都是变量// { x: 100, y: 200, z: 'abc' }console.log(obj2)let obj3 = {x: x, y, [z]: z} // 想要使用z变量的值作为key,需要加中括号console.log(obj3)// { x: 100, y: 200, abc: 'abc' }
注意:对象的key只能是字符串类型,最后都会被转换成字符串。
function Point(x, y){ this.x = x this.y = y console.log('Point~~~~~~~~~~~')}console.log(Point) // 只有[Function: Point]p1 = new Point(4, 5) // 实例化,如果前面没有new,则只是普通的函数调用,那么打印出的undefined;加new后,后面的就是构造器console.log(p1)p1.show()/*Point~~~~~~~~~~~Point { x: 4, y: 5, show: [Function] }*/console.log('------------------')function Point3D(x, y, z){ Point.call(this, x, y) // 继承,此时的this是Point3D的实例 this.z = z console.log('Point3D~~~~~~~~~~~~~')}console.log(Point3D)p2 = new Point3D(10, 11, 12)console.log(p2)p2.show()/*[Function: Point3D]Point~~~~~~~~~~~Point3D~~~~~~~~~~~~~Point3D { x: 10, y: 11, show: [Function], z: 12 }Point3D { x: 10, y: 11, show: [Function], z: 12 } 10 11*/
new构建一个新的通用对象,new操作符将新的对象的this值传递给Point3D构造器函数,函数为这个对象创建类z属性。new后得到一个对象,使用这个对象的this来调用构造器,那么如何执行“基类”的构造器方法呢?使用point3D对象的this来执行Point对象的构造器,所以使用call方法,传入子类的this。
从ES6开始,新提供了class关键字,使得创建对象更加简单、清晰。
class Point{ constructor(x,y){ // 构造器 this.x = x this.y = y this.show = () => {console.log("Point")} } show(){ // 方法 console.log(this, this.x, this.y) }}let p3 = new Point(1, 2) // class 定义的是新语法,必须使用new关键字实例化p3.show()// Point { x: 1, y: 2 } 1 2class Point3D extends Point{ constructor(x, y, z){ super(x, y) this.z = z this.show = () => {console.log("Point3D")} } show(){ console.log(this, this.x, this.y, this.z) // 重写了子类的方法,会覆盖父类的方法 }}let p4 = new Point3D(3, 4, 5)p4.show()// Point3D { x: 3, y: 4, z: 5 } 3 4
注意:父类、子类使用同一种方式定义属性或方法时,子类覆盖父类。访问同名属性或方法时,优先使用属性。
class Add{ constructor(x, y){ this.x = x this.y = y } static print(){ // 把此处的静态方法理解为python中的classmethod console.log(this, this.x) // this为[Function: Add],不是实例,就是说this指向类,不是类的实例 }}add = new Add(10, 20)console.log(Add) // [Function: Add]Add.print() // 结果为[Function: Add] undefined =>因为类没有定义x属性// add.print() // 注意这里与python不同,实例不能直接访问静态方法,与c++、java是一致的add.constructor.print() // 实例访问静态方法,需要借助构造器方法constructor
JS中静态的概念与pyhton的静态方法不同,相当于python中的类方法(classmethod)
4.this的坑
虽然js和c++_、java一样有this,但是js的表现是不同的。原因在于,c++、java是静态编译型语言,this是编译期绑定,而js是动态语言,运行期绑定。
var school = { name : 'magedu', getNameFunc: function(){ console.log(this.name) console.log(this) return function(){ console.log(this === global) // this是否是global对象 return this.name } }}console.log(school.getNameFunc()())/*magedu{ name: 'magedu', getNameFunc: [Function: getNameFunc] }true // 此处是true说明this是全局变量,是global,没有name属性,所以最后打印undefinedundefined*/
为了分析上面的程序,先学习一些知识:函数执行时,会开启新的上下文环境ExecutionContext。创建this属性,但是this是什么就要看函数怎么调用了。
我们在使用时,有时候需要明确的让this必须是我们期望的对象,该如何做呢?解决上述的方法有很多:
显示传入:
var school = { name : 'magedu', getNameFunc: function(){ console.log(this.name) console.log(this) return function(that){ console.log(this == global) // this是否是global对象 return that.name } }}console.log(school.getNameFunc()(school))/***magedu**{ name: 'magedu', getNameFunc: [Function: getNameFunc] }**true **magedu*/
通过主动传入对象,避开了this的问题。
ES3引入了apply、call方法:
function Print(){ this.print = function(x, y) {console.log(x + y)}}p = new Print(100, 200)p.print(10, 20)p.print.call(p, 10, 20) // 30p.print.apply(p, [10, 20]) // 30// apply、call方法都是函数对象的方法,第一参数都是对象的实例,apply传其他参数需要使用数组/*magedu{ name: 'magedu', getNameFunc: [Function: getNameFunc] }false */var school = { name : 'magedu', getNameFunc: function(){ console.log(this.name) console.log(this) return () =>{ console.log(this == global) // this是否是global对象 return this.name } }}console.log(school.getNameFunc().call(school))/*mageduschool { name: 'magedu' } 'object'falsemagedu*/
apply、call方法都是函数对象的方法,第一参数都是传入对象的实例。apply传其他参数需要使用数组。call传其他参数需要使用可变参数收集。
ES5引入bind方法:
var school = { name: 'magedu', getNameFunc: function(){ console.log(this.name) console.log(this) return function (){ console.log(this === global) return this.name } }}var func = school.getNameFunc()console.log(func)/*magedu{ name: 'magedu', getNameFunc: [Function: getNameFunc] }[Function]*/var new_func = func.bind(school) // 绑定后会返回新的函数console.log(new_func) // [Function: bound ]console.log(new_func())/*falsemagedu*/
apply、call方法,参数不同,调用时传入this。bind方法是为函数先绑定this,调用时直接用。
ES6引入支持this的箭头函数:
// ES6新的定义class school{ constructor(){ this.name = 'magedu' } getNameFunc(){ console.log(this.name) console.log(this, typeof this) // this是object return () => { console.log(this === global) // this不是全局变量 return this.name } }}console.log(new school().getNameFunc()())/*mageduschool { name: 'magedu' } 'object'falsemagedu*/
转载地址:http://svfvi.baihongyu.com/