JS 基础进阶

JS 基础进阶

文章目录

一、原型与原型链

如何准确判断一个变量是不是数组:类型判断-instanceof
class的原型本质,怎么理解

1.1 类和继承

1.实例属性和方法

es5:

function People(name,age){
	this.name = name
	this.age = age							//实例属性	
	this.walk = function() {				//实例方法
        console.log( `I am walking` )
    }		
}
People.prototype.show = function(){			//实例方法
	console.log(this.name)
}

let p = new People('xx',18)  

对于类中的属性,可以直接在 constructor 中通过 this 直接定义,还可以直接在类的顶层来定义(Setters & Getters):

es6:

class People{
	constructor(name,age){
		this.name = name
		this.age = age
		this._sex = 0
    }
    get sex() {					//只读
        return this._sex
    }
    set sex(val) {				//可写
    	//可设置业务逻辑
        this._sex = val
    }
}
	show(){
		console.log(this.name)
	}
}

这个代码演示了,通过 get/set 来给类定一个属性,不过貌似没有说服力。因为 age 和 _sex 都是类的属性,而且是相同的含义这样做感觉没有实际用途。但是如果一个属性是个只读的呢?

class Animal {
    constructor(type) {
        this.type = type
    }
    get addr() {
        return '北京动物园'
    }
}

毋庸赘述,大家都能看出来含义。再来看下如下的应用场景:

class CustomHTMLElement {
    constructor(element) {
        this.element = element
    }
    get html() {
        return this.element.innerHTML
    }
    set html(value) {
        this.element.innerHTML = value
    }
}

利用 set/get 实现了对 element.innerHTML 的简单封装。

可是,有时候我们真的需要设置一个私有属性(闭包),然后通过一定的规则来限制对它的修改,利用 set/get就可以轻松实现。

let #age = 1
class Animal {
    constructor(type) {
        this.type = type
    }
    get age() {
        return #age
    }
    set age(val) {
        if (val > 0 && val < 10) {
            #age = val
        }
    }
}

2.静态属性和方法
es5:

People.count = 0				//静态属性
function People(name,age){
	this.name = name
	this.age = age											
}
People.getCount = function(){	//静态方法
	console.log(People.count)
}
let p = new People('xx',18)  

es6:无静态属性

function People(name,age){
	this.name = name
	this.age = age								
	static eat() {											//静态方法
        console.log( `I am eating` )
    }
}

3.类的继承

//es5:
function Student(name,occupation){
	People.call(this,occupation)	//子类继承父类属性name
	this.occupation = occupation	
}
Student.prototype = new People()	//子类继承父类属性name
Student.prototype.constructor = Student	

es6:

class Student extends People{
  constructor (name,occupation) {
    super(name)
    this.occupation = occupation	
  }
  run () {
    console.log('I can run')
  }
}

es6中,class 的类型还是 function,和 es5 貌似并没有什么区别,那么 class 中定义的方法在哪呢?我们知道只要是函数,就一定会有 prototype 对象。那么类的方法和 prototype 对象有什么关系呢?

console.log(Animal.prototype)
// {constructor: ƒ, walk: ƒ}
//   constructor: class Animal
//   walk: ƒ walk()
//   __proto__:
//   constructor: ƒ Object()
//   hasOwnProperty: ƒ hasOwnProperty()
//   isPrototypeOf: ƒ isPrototypeOf()
//   propertyIsEnumerable: ƒ propertyIsEnumerable()
//   toLocaleString: ƒ toLocaleString()
//   toString: ƒ toString()
//   valueOf: ƒ valueOf()
//   __defineGetter__: ƒ __defineGetter__()
//   __defineSetter__: ƒ __defineSetter__()
//   __lookupGetter__: ƒ __lookupGetter__()
//   __lookupSetter__: ƒ __lookupSetter__()
//   get __proto__: ƒ __proto__()
//   set __proto__: ƒ __proto__()

可以看出在 Animal.prototype 对象上有两个方法,一个是构造函数(constructor)、一个是自定义的方法(walk)。这是不是和 ES5 的第二种写法一模一样?我们再来看下属性,在 ES5 中有个 API 用来判断对象的自有属性(hasOwnProperty)。

console.log(dog.hasOwnProperty('type')) //true

这个表现也和 ES5 中直接使用 function 定义类的方式相同,所以得出一个结论:class 的方式是 function 方式的语法糖。

虽然 ES6 在类的定义上仅是 ES5 定义类的语法糖,但是从开发者的角度而言,开发更有效率了,代码可阅读性大大提升。

练习
请实现一个堆栈类,具备 push、pop 功能。
请回忆下自己在业务中有哪些场景可以用类来实现。

1.2 隐式原型与显式原型

JS 基础进阶
JS 基础进阶

1.每个实例都有隐式原型实例.__proto__每个类都有显式原型类.prototype
2.实例的隐式原型__proto__指向类的显示原型prototype

1.3 原型链

JS 基础进阶
JS 基础进阶
获取对象属性xialuo.name或执行方法xiaoluo.sayHi()时,先在自身属性和方法找,若找不到再到__proto__中查找
注意:类中定义的方法属于类的显示原型属性prototype,当实例化对象以后,该对象中不存在此方法属性,只存在构造函数中的属性以及隐式原型__proto__属性,且该隐式原型属性指向类的显示原型属性


二、this的应用场景

==this的取值不是在定义的时候确定,而是在函数执行的时候确定

2.1 普通函数中使用

function f5(){
    console.log('this=',this)    //window对象
}
f5()                   
f5.call({x:100})
f5.bind({x:200})()

注意bind函数和call函数能改变this的取值

2.2 在class的方法中调用

尤其注意在类方法中应用setTimeout函数时,普通函数与箭头函数的区别:

class Person {
       constructor(name,age){
              this.name = name
              this.age = age
       }
       say(){
              console.log(`我是${this.name},今年${this.age}岁`)
       }
       wait(){
              setTimeout(function f() {
                     console.log('setTimeout中this=',this)  //window    
              })
       }
}

class Dog {
      constructor(name){
             this.name = name    
      }
 
      wait(){
             setTimeout(
                    () =>{
                    console.log('setTimeout中this=',this) //当前对象   
                    }      
             )
      }
}

2.3 简易的jQuery代码实现 :

 class jQuery {
         constructor(selector){
                const selectors = document.querySelectorAll(selector)
                const length = selectors.length
                for(let i=0;i<length;i++){
                       this[i] = selectors[i]
                }

                this.length = length
         }

         each(fn){
                for(let i=0;i<length;i++)
                       fn(this[i])      
         }

         on(type,fn){
                
                return this.each(elem=>{
                       elem.addEventListener(type,fn,false)})                                      
         }
  }
                            
上一篇:ES6中的class 与prototype


下一篇:JS中的继承