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 隐式原型与显式原型
1.每个实例都有隐式原型:实例.__proto__
;每个类都有显式原型:类.prototype
2.实例的隐式原型__proto__指向类的显示原型prototype
1.3 原型链
获取对象属性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)})
}
}