文章目录
- 面向对象
- ES2015的面向对象语法:
- ES5的面向对象语法:
- 原型对象
- 原型链
- Object对象的原型
- this关键字
- **指向调用方法的对象:**
- **构造函数:**
- 触发事件
- 指向全局对象:
- 箭头函数
- 总结
- bind appl call方法
- call的用法
- 参数
- apply
- bind
- 总结
面向对象
ES2015的面向对象语法:
(js可以不适用面向对象的方式开发)
- 类
- 对象实例化
- 构造函数
- 方法
- 继承
class User {
constructor(username, password) {
this.username = username;
this.password = password
}
login() {
if(this.username === "xiaoming" && this.password === "123456") {
console.log("登录成功")
}else {
console.log("登录失败")
}
}
}
let user = new User("xiaoming", "123456")
user.login()
class Admin extends User {
constructor(username, password, id) {
super(username, password)
this.id = id
}
crudUser() {
console.log("对用户进行操作")
}
}
let admin = new Admin("xiaoming", "123456", 66)
admin.login()
admin.crudUser()
console.log(admin.id)
总结:
- 大部分前端工程师,在工作的过程中,都是用面向过程的方式去解决问题的。因为面向过程代码更简洁,解决问题的方式更直接。
- 面向对象的程序设计会大幅度提升代码量,但是如果我们开发的系统,经常会需求变更,功能扩展,或是修复BUG,那么面向对象的程序会有很大的优势。
也就是说,如果问题规模上升,面向对象对于系统的可扩展和可维护性优势是非常突出的。
ES5的面向对象语法:
- 构造函数来模拟一个类
- 对象实例化: new + 构造函数
- 继承: prototype
function User(username, password) {
this.username = username
this.password = password
this.login = function() {
if(this.username === "xiaoming" && this.password === "123456") {
console.log("登录成功")
}else {
console.log("登录失败")
}
}
}
var user = new User("xiaoming", "123456")
user.login()
function Admin() {
}
// prototype
原型对象
- 原型对象是一个对象,所有的对象都会从原型对象上继承属性和方法
- 我们可以使用构造函数(类)的prototyp属性,或者对象的__proto__属性获取到它
function User() {}
// 使用构造函数的prototype属性获取原型对象
// User.prototype = {name: "xiaoming"}
var user = new User()
// 通过user的__proto__属性来获取原型对象
user.__proto__ = {name: "zhangsan"}
console.log(user.name); // zhangsan
// es 2015
class Test{}
Test.prototype.name = "lisi"
let test = new Test()
console.log(test.name); //lisi
prototype用来扩展内置对象的功能
// 原型的方式实现对象的继承
function User(){
this.login = function() {
console.log("登录成功")
}
}
function Admin() {
}
Admin.prototype = new User()
var admin = new Admin()
admin.login()
原型链
每个对象都可以有一个原型,这个原型还可以有它自己的原型,以此类推,形成一个原型链。查找特定属性的时候,我们先去这个对象里去找,如果没有的话就去它的原型对象里面去找,如果还是没有的话再去向原型对象的原型对象里去寻找。
function Person() {
this.id = "007"
}
function User(){
this.login = function() {
console.log("登录成功")
}
}
User.prototype = new Person()
function Admin() {
}
Admin.prototype = new User()
var admin = new Admin()
console.log(admin.id)
Object对象的原型
- 所有原型链的终点都是Object.prototype
- 只要在Object对象的原型上添加属性或方法,所有对象都可以使用。
内置对象的方法是通过原型对象定义的:
- Array: push join
- Date: getMinutes, getHours
- Function: call apply bind
- Object : toString
尝试:重写Array的push方法,输出“hello world”
重写function的toString方法
通过prototype扩展内置对象:
为Date对象添加一个dateFormate方法,使其输入xxx年xx月xx日
this关键字
this关键字是一个引用,它指向内存中的一个对象。
指向调用方法的对象:
最常用的一种情况,一个对象调用了一个方法,那么方法中的this,指向这个对象。
let cat = {
name:"zhangsan",
sayName() {
console.log(this)
}
}
cat.sayName()
构造函数:
构造函数中的this,指向new创建出来的对象
function Cat (name) {
this.name = name
}
var cat = new Cat('lisi')
console.log(cat.name)
触发事件
DOM事件被触发时,事件监听函数中的this指向触发事件的DOM对象。
指向全局对象:
- 在全局环境,this指向全局对象window
- 在函数中,this同样指向window对象,因为没有从属于某一个对象的函数,是window的方法
setTimeout中的this也是指向window (除非里面的function使用箭头函数)
箭头函数
- 箭头函数中的this,指向函数定义时的上下文
- 通常会在内部函数中使用,异步数据交互时会频繁使用(ajax,或计时器)
总结
this关键字的指向问题:
- 全局环境下指向全局对象
- 全局环境中的this,指向全局对象
- 内部函数中的this,指向全局对象
- 方法中的this,指向调用方法的对象
- 事件中的this,指向触发事件的DOM对象
- 构造函数中的this,指向new创建对象
- 箭头函数中的this,指向定义函数上下文的this
bind appl call方法
call、apply、bind都是函数的方法,改变函数内部this的指向:
call的用法
call是函数(方法)的方法,通过call,可以改变this的指向,让this指向call的第一个参数
- 全局函数调用call
- 对象的方法调用call
function fun () {
console.log(this); // window
}
let cat = {
name: 'miao'
}
fun.call(cat); // call的第一个参数是对象
-----
let cat = {
name: 'miao'
}
let dog = {
name:'wang',
sayName() {
console.log("我是" + this.name)
}
}
dog.sayName.call(cat)
实际开发中call如何使用?
利用call来实现继承
// es2015 extends
// es5 原型链
// call 继承 多重继承
function User() {
this.crudContent = function(){
console.log("增删改查")
}
}
function Person() {
this.login = function() {
console.log("登录成功")
}
}
function Admin() {
User.call(this)
Person.call(this)
}
var admin = new Admin()
admin.crudContent()
admin.login()
参数
let cat = {
name: 'miao'
}
let dog = {
name: 'wang',
sayName(food1, food2) {
console.log(`我是${this.name}, 我喜欢吃${food1}和${food2}`)
}
}
dog.sayName('骨头', '肉')
dog.sayName.call(cat, '鱼', '冻干')
apply
跟call一样 除了传参数不一样
let cat = {
name: 'miao'
}
let dog = {
name: 'wang',
sayName(food1, food2) {
console.log(`我是${this.name}, 我喜欢吃${food1}和${food2}`)
}
}
dog.sayName('骨头', '肉')
dog.sayName.apply(cat, ['鱼', '冻干'])
bind
bind传参跟call一样
但是不会直接运行函数,而是把函数运行结果作为返回值返回。
let cat = {
name: 'miao'
}
let dog = {
name: 'wang',
sayName(food1, food2) {
console.log(`我是${this.name}, 我喜欢吃${food1}和${food2}`)
}
}
dog.sayName('骨头', '肉')
let newFun = dog.sayName.bind(cat, '鱼', '冻干')
newFun()
总结
- call 会调用函数,通过参数列表依次传参
- apply 会调用函数,是通过数组来传递参数
- bind, 不会调用函数,将新的重新绑定this的函数作为返回值,通过参数列表依次传参