数据类型
JS数据类型包括基本数据类型和引用数据类型
- 基本数据类型包括string,number,boolean,undefined,null,symbol
- 引用类型包括object (array[1,2] function(){})
二者的区别是什么
- 基本数据类型占有固定的大小,存在栈中,赋值的方式传值,赋值后不会相互影响
- 引用类型的大小不固定,在堆中储存指针的指向,赋值后相互影响
检测数据类型的方式:typeof,instancsof,constructor
console.log(typeof 2); // number
console.log(typeof true); // boolean
console.log(typeof 'str'); // string
console.log(typeof []); // object
console.log(typeof function(){}); // function
console.log(typeof {}); // object
console.log(typeof undefined); // undefined
console.log(typeof null); // object
数组和对象和null会判断为object
console.log(2 instanceof Number); // false
console.log(true instanceof Boolean); // false
console.log('str' instanceof String); // false
console.log([] instanceof Array); // true
console.log(function(){} instanceof Function); // true
console.log({} instanceof Object); // true
instancof 只可以判断引用类型的数据类型
console.log((2).constructor === Number); // true
console.log((true).constructor === Boolean); // true
console.log(('str').constructor === String); // true
console.log(([]).constructor === Array); // true
console.log((function() {}).constructor === Function); // true console.log(({}).constructor === Object); // true
constructor有两个作用,一是判断数据的类型,二是对象实例通过 constrcutor 对象访问它的构造函数。
字符串的操作方法
- .length 获取字符串的长度
var str = 'abcdef'
str.length // 6
- indexOf(searchvalue,fromindex) 查找某个字符所在的索引值
var str = 'abcdefga'
console.log(str.indexOf("a")) //0
console.log(str.indexOf("z")) //-1
console.log(str.indexOf("a", 3)) //7
- includes(searchvalue, start) 查找字符串是否包含特定的子字符串
var str = 'abcdefg'
console.log(str.includes('a')) //true
console.log(str.includes('z')) //false
console.log(str.includes('a',4)) //false
- concat(str1,str2,...,strx) 连接多个字符串
var str1 = 'abc';
console.log(str1.concat('efg')) //abcefg
console.log(str1.concat('efg','sss')) //abcefgsss
- .split(separator,limit) 把一个字符串分割成一个字符串数组,且不会改变原字符串
let str = 'abcdef'
str.split('c') //['ab','def']
str.split("",4) //['a],'b','c','d']
- slice(start,end) 截取字符串,并不会改变原字符串,获取数据的范围
let str = 'abcdef'
str.slice(0,2) //abc
str.slice(1) //bcdef
str.slice(-2) //ef
- substr(start,length),获取数据的范围是【strat,end)
- substring(form,to),获取的数据范围是(start,end】
- toLowerCase(),toUpperCase() 转化大小写
- replace(),match(),search()字符串模糊匹配
- trim() 去除字符串首尾的空格,该方法不会改变原始字符串
数组的操作方法
- 会改变原数组 push(),pop(),shift(),unshift(),reverse(),splice(),sort()
- 不会改变原数组 reduce(),filter(),every(),some(),find(),findIndex(),join(),map(),forEach(),includes()
改变原数组的方法
- push()&&unshift() 从尾部/头部添加元素 返回值是新数组的长度
var arr = [1,2,4,5,87,8]
arr.push(33) //7 新数组的长度 arr = [1,2,4,5,87,8,33]
arr.unshift(99) // 8 新数组的长度 arr=[99,,1,2,4,5,87,8,33]
- pop()&&shift() 删除数组的最后一个/第一个元素,返回值是被删除的元素
var arr = [1,2,4,5,87,8]
arr.pop() // 8 返回被删除的元素 arr=[1,2,4,5,87]
arr.shift() // 1 返回被删除的元素 arr=[2,4,5,87]
- reverse() 颠倒数组元素的方法
var arr = [1,2,4,5,87,8]
arr.reverse() // [8,87,5,4,2,1]
- splice() 增加/删除元素 返回被删除的元素
array.splice(index,howmany,item1,.....,itemX)
var arr = [1,2,4,5,87,8]
arr.splice(2,2) //[4,5] 返回被删除的元素
arr.splice(0,1,99) [1] arr=[99,2,87,8]
- sort() 对数组进行排序 返回新的数组
var arr = [1,2,4,5,87,8]
arr.sort() // [1,2,4,5,8,87]
arr.sort((a,b)=>b-a) // 从大到小排序
arr.sort((a,b)=>a-b) // 从小到打排序
不会改变原数组的方法
- reduce() 主要于对数组的求和,也可以对字符串,数组进行拼接
Array.reduce((accumulator,current,index,Array) =>{
....... return accumulator } ,int)
accumulator:当前的聚合值;current:循环的当前的元素;index:索引值 arr:数组本身;int: 初始值
//数组求和
var arr = [1,2,4,5,87,8]
arr.reduce((pre,cur)=>{ return pre+cur},0 ) //107
//数组对象求和
var arr = [{b:3},{b:5}]
arr.reduce((pre,cur)=>{return pre + cur.b},2) // 10
- filter() 从数组中筛选符合条件的元素
array.filter(function(currentValue, indedx, arr), thisValue)
var arr = [1,2,4,5,87,8]
arr.filter(item=>item>4) //[5,87,8] 返回符合条件的元素 (浅拷贝)
- ervey(),some() 前者只有元素都符合条件返回true否则为false;后者只要有个元素符合条件就返回true,都不符合则返回false
var arr = [1,2,4,5,87,8]
arr.every(it=>it>0) //true 数组的每个元素都大于0
arr.some(it=>it>90) // false 数组只要有一个符合条件就返回TRUE
- find() findIndex() 返回符合条件的第一个元素/元素下标
array.find(function(currentValue, index, arr),thisValue)
var arr = [1,2,4,5,87,8]
arr.find(it=>it>0) //1
arr.find(it=>it>99) //undefined
arr.findIndex(it=>it>0) //0
arr.findIndex(it=>it>99) //-1
- join() 将元素按照字符串连接起来返回
var arr = [1,2,4,5,87,8]
arr.join() //'1,2,4,5,87,8'
arr.join(' ') //'1 2 4 5 87 8'
map()和foreach()的区别
相同点:
- forEach()和map()方法通常用于遍历Array元素
- 都不能使用break打断,中断遍历
- 不会改变原数组
不同点:
- foreach()返回undefined,map()返回一个新数组
- map()可以链接其他方法,比如reduce().sort(),filter()
解决if嵌套的方法
- switch case
- 用return代替else if
- array的includes()
深拷贝和浅拷贝
深拷贝和浅拷贝是针对引用类型的
浅拷贝:是拷贝指针的指向,共享同一块内存,改变新对象旧对象也会改变
- Object.assign()
- Array.from()
深拷贝:是复制并创建一个新的对象,不共享内存,修改新对象,旧对象不改变
- JSON.parse(JSON.stringify())
let B = JSON.parse(JSON.stringify(A))
- 遍历循环
- slice()
- concat()
- es6运算扩展符...
数组去重的方法
- es6的Set去重
let arr = [1,2,3,1,2,3]
let arr2 = Array.from(new Set(arr)) //[1,2,3]
- 双重for循环,然后用slice去重
let arr = [1,2,3,1,2,3]
for(var i=0;i<arr.length;i++){
for(var j=i+1; j<arr.length; j++){
if(arr[i] === arr[j]){
arr.splice(j,1);
j--
}
}
}
//arr=[1,2,3]
- indexOf去重
let arr = [1,2,3,1,2,3]
let arr2 = [];
for(var i = 0; i<arr.length; i++){
if(arr2.indexOf(arr[i])) === -1){
arr2.push(arr[i])
}
}
- filter去重
let arr = [1,2,3,1,2,3]
arr2 = arr.filter((it,index,self)=>{
return self.indexOf(it) === index;
})
var let consr的区别
- 作用域不同:var的作用域是全局,let和const的作用域是块级作用域
- 变量提升: var定义的变量会发生变量提示,let和const则不会
console.log(num); //undefined
var num = 1;
此时的console.log()没有报错,原因是num进行了变量提升 等价于:
var num;
console.log(num);
num = 1;
- 重复声明:var可以重复声明,后声明的会覆盖先声明的值;let和const不可以重复声明。
- 初始值:var和let可以不设置初始值,const必须声明初始值,并且不允许更改
- 指针的指向:let和const都是es6新声明的语法。let可以更改指针的指向(即可以改值),const不可以
- 暂时性死区:let和const命令在声明变量之前是不可以使用的。在语法上称为暂时性死区;var则没有
解构
解构是 ES6 提供的一种新的提取数据的模式,这种模式能够从对象或数组里有针对性地拿到想要的数值。
-
数组解构:由元素的位置为匹配条件来提取想要的数据
const [a,b,c] = [1,2,3] //a:1 b:2 c:3
const[a,,c]=[1,2,3] //a:1 c:3
-
对象解构:由对象的属性为匹配条件来提取数据
const student = {
age:18;
name: 'huahua'
}
const {name,age} = student;
提取高嵌合度的对象属性:通过冒号:+{对象属性名}的方式来获取
const school = {
classes: {
stu:{
age:10;
name: 'amy';
}
}
}
const {classes: {stu: {name} } = school;
//name:amy
箭头函数
运用箭头来定义函数 ()=> {}
箭头函数的参数
- 没有形参 let fun1=()=> { console.log('aaaaaaaaaa') };
- 有一个形参 let fun2 = name=>{ console.log(
Hello ${name} !) } - 有多个形参 let fun3 = (name,age)=> { return [age,name]; }
箭头函数的函数体
- 如果箭头函数的函数体只有一句代码,仅仅是返回一个值或一个简单的表达式,可以省去函数的大括号{}
let fun1 = val => val
等价于
let fun2 = function (val) { return val }
let sum1 = (num1,num2) => num1 + num2;
等价于
let sum2 = function(num1,num2){ return num1 + num2 };
- 如果箭头函数的函数体只有一句代码,只返回一个对象,可以用小括号包裹,但是不可以直接用大括号包裹
let getItem = id=> ({id:id,name:'Tom'})
let getItem = id=> {id:id,name:'Tom'} //这个写法是错误的 大括号会解析为函数体的大括号
-如果箭头函数的函数体只有一句代码,且不需要返回值(常见的是调用一个函数),可以给这条语句的前面加一个void关键字。
let fun = void noReturnFun();
箭头函数常用的作用就是简化回调函数
//普通函数写法
[1,2,3].map(function(x){
return x*x
})
//箭头函数写法
[1,2,3].map( x=> x*x );
箭头函数和普通函数的区别
- 语法更简洁清晰
- 箭头函数不会创建自己的this : 箭头函数没有自己的this,它会捕捉自己在定义时(注意是定义时不是调用时)所处的外层执行环境的this,并继承this值。所以箭头函数的this值在定义时就确定了,不会再改变
var id = 'Global';
function fun1 (){
setTimeout(function(){
console.log(this.id); //Global
},2000)
}
function fun2 (){
setTimeout(()=>{
console.log(this.id); //Obj
},2000)
}
fun1.call({id:'Obj'});
fun2.call({id:'Obj'});
在fun1这个函数是在全局调用的,所以this指向window对象,this.id就指向全局变量id即Global; fun2中的箭头函数的this在定义时就确定了,继承了外层fun2的执行环境中的this,而fun2在调用时this被call方法改变到了对象{id:'Obj'}中,所以输出Obj
var id = 'GLOBAL';
var obj = {
id: 'OBJ',
a: function(){
console.log(this.id);
},
b: () => {
console.log(this.id); } };
obj.a(); // 'OBJ'
obj.b(); // 'GLOBAL'
- 箭头函数继承而来的this指向永远不变!
- .call(),.apply(),.bind()无法改变箭头函数的this指向
var id = 'Gloabl';
let fun1 = ()=>{
console.log(this.id)
}
fun1();
this的指向不会改变,一直指向widow对象
fun1.call({id: 'Obj'}); // 'Global'
fun1.apply({id: 'Obj'}); // 'Global'
fun1.bind({id: 'Obj'})(); // 'Global'
- 箭头函数不能作为构造函数使用
构造函数的new:①js首先会生成一个对象②再把函数中的this指向该对象③执行构造函数中的语句④返回该对象的实例。 箭头函数是没有自己的this,继承于外层执行环境的this,且不会通过调用而改变,所以箭头函数不能作为构造函数。即构造函数不能用箭头函数定义,否则在new调用时会报错
let Person = (name,age)=>{
this.name = name;
this.age = age;
}
let p = new Person('huahua',37); //报错
-
箭头函数没有自己的argument:
箭头函数没有自己的arguments,可以获取外层局部函数的执行环境中的值。 arguments是一个相对于传递给函数的参数的类数组对象。它是一个对象不是一个Array,但是有length和索引的属性
let fun = (val)=>{
console.log(val); //111
console.log(arguments); //报错
}
fun (1111)
function outer(val1,val2){
let arr = aguments;
cosnole.log(arr); // [111,222]
let fun = ()=>{
let arr2 = arguments; //获取了上一层函数的arguments
console.log(arr2); //[111,222]
console.log(arr1 === arrr2); //true
}
}
outer(111,222)
可以在箭头函数中使用rest参数代替arguments对象,来访问箭头函数的参数列表!!
- 箭头函数没有原型prototype
let sayHi = ()=>{
console.log('helloworld');
}
console.log(sayHi.prototype) //undefined
原型和原型链
原型:
js中是使用构造函数来创建一个新对象,每一个构造函数的内部都有一个prototype的属性,它的属性值是一个对象,包括构造函数的所有实例共享的属性和方法。当使用构造函数新建一个对象后,这个对象的内部就会包含一个指针,这个指针指向构造函数的prototype属性所对应的值,这个指针被称为原型。新建的对象可以通过_proto_这个属性来访问。但是最好不要使用,不是规范中规定的。es5新增了一个Object.getPrototypeOf()方法来获取原型。
原型链:
当访问一个对象的属性时,如果对象内部不存在这个属性,它就会去他的原型对象里去找,这个原型对象又会有自己的原型对象,于是一直这样找下去,也就是原型链的概念。原型链的一般尽头来说都是Object.prototype。这就是为什么新建的对象可以使用toString()的方法。
特点
js的对象都是通过引用来传值的,创建的每一个新对象没有属于自己的原型副本。所以当修改原型时,与之相关的对象都会继承这一改变。
- 原型的修改和重写
function Person(name){
this.name = name;
}
//修改原型
Person.prototype.getName = function(){}
var p = new Person('xiaoming');
console.log(p._proto_ === Person.prototype) //true
cosnole.log(p._proto_ === p.constructor.prototype) //true
//重写原型
Person.prototype = {
getName: function(){};
}
var p = new Person('xiaoming');
console.log(p._proto_ === Person.prototype) //true
cosnole.log(p._proto_ === p.constructor.prototype) //false
可以看到在修改原型时候,p的构造函数不指向Person,因为直接给Person的原型对象用对象赋值,它的构造函数指向了根构造函数Object,所以这个时候,p.constructor === Object。想要成立p.constructor === Person,需要用constructor指回来。
Person.prototype = {
getName: function() {}
}
var p = new Person('hello')
p.constructor = Person
console.log(p.__proto__ === Person.prototype) // true
console.log(p.__proto__ === p.constructor.prototype) // true
- 原型链的指向
p.__proto__ // Person.prototype
Person.prototype.__proto__ // Object.prototype
p.__proto__.__proto__ //Object.prototype
p.__proto__.constructor.prototype.__proto__ // Object.prototype
Person.prototype.constructor.prototype.__proto__ // Object.prototype
p1.__proto__.constructor // Person Person.prototype.constructor // Person
-
原型链的终点是什么
由于Object是构造函数,原型链的终点是Object.prototype.proto,而Object.prototype.proto === null,所以原型链的终点是null。
-
如何获取对象上非原型链上的属性
使用hasOwnProperty()方法来判断属性是否属于原型链的属性:
function isExist(obj){
var res=[];
for(var key in obj){
if(obj.hasOwnProperty(key))
res.push(key+': '+obj[key]);
} return res;
}
事件循环机制
首先JavaScript是一个单线程语言。分为两个任务种类:同步任务和异步任务。
同步任务:只要被扫描到就会被主线程立刻执行的任务,优先于所有异步任务。
异步任务:即使被扫描到,也不会立马执行,会压入异步任务队列中,等到主线程的任务全部清空了,再被召唤执行。
异步任务分为宏任务和微任务。在异步任务中平均执行周期很长的任务被称为宏任务,而平均执行周期较短的任务称为微任务。
当有异步任务被压入异步任务队列中时,JavaScript会将这些任务分为宏任务和微任务俩个新的队列。然后等所有同步任务执行完成后,异步任务会优先执行在任务队列中的微任务。在所有微任务执行完毕后,再去宏任务队列中执行一个宏任务(注意是一个),执行完一个宏任务再去微任务队列中检查是否有新的微任务,有则全部执行,再回到宏任务中执行一个宏任务,以此循环。这一套流程就叫事件循环(event loop)
- 宏任务:setTimeout(),setInterval()
- 微任务:Promise.then() aysnc/await
setTimeout(()=>{
console.log('1')
},0) //异步任务的宏任务
console.log('2') //同步任务
Promise.resolve().then(()=>{
console.log('3')
}) //异步任务的微任务
console.log('4') //同步任务
//2 4 3 1
//第一个宏任务
setTimeout(() => {
console.log(1); //宏任务中的同步任务
Promise.resolve().then(() => { console.log(7) }) //宏任务中的微任务
}, 0); //异步任务 - 宏任务
console.log(2); //同步任务
Promise.resolve().then(() => { console.log(3) }) //异步任务 - 微任务
//第二个宏任务
setTimeout(() => {
console.log(8); //宏任务中的同步任务
setTimeout(() => { console.log(5) }, 0) //宏任务中的宏任务 第四个宏任务
}, 0);
//第三个宏任务
setTimeout(() => {
Promise.resolve().then(() => { console.log(4) }) //宏任务中的微任务
}, 0);
console.log(6); //同步任务
// 2 6 3 1 7 8 4 5
async function async1() {
console.log('async1 start') //2
await async2()
console.log('async1 end') //6
}
async function async2() {
console.log('async2') //3
}
console.log('script start') //1
setTimeout(function() {
console.log('setTimeout') //8
}, 0)
async1();
new Promise( function( resolve ) {
console.log('promise1') //4
resolve();
} ).then( function() {
console.log('promise2') //7
} )
console.log('script end') //5
https://blog.csdn.net/qq_41681425/article/details/85775077
个人认为最需要注意的细节是,事件循环每一次只执行一个宏任务
console.log(1)
setTimeout(() => {
console.log(2)
new Promise(function(resolve) {
console.log(3)
resolve();
}).then(function(){
console.log(4)
})
});
new Promise(function(resolve){
console.log(5)
resolve()
}).then(function(){
console.log(6)
})
setTimeout(() => {
console.log(7)
new Promise(function(resolve){
console.log(8)
resolve('ssss')
}).then(function(res){
console.log('结果是'+res)
console.log(9)
})
});
console.log(10)
// 1-5-10-6-2-3-4-7-8-结果是sss-9
new Promise((resolve,reject)=>{
setTimeout(() => {
resolve('res11')
reject("dfjfgdsgfsg")
}, 3000);
}).then(res=>{
console.log(res)
})
console.log('start')
new Promise(resolve=>{console.log('promise')})
async function newFunc(){
await async2();
console.log('11111111111')
new Promise((resolve,reject)=>{
resolve('res22')
setTimeout(() => {
console.log('WAITING')
}, 2000);
console.log('settimeout')
}).then(res=>{
console.log(res+'DDDDDDDDDD')
})
console.log('222222222222')
}
function async2(){
console.log('3333333333')
}
console.log('midden')
newFunc();
console.log('ending')
//start>promise>midden>3333333>ending>11111111>settimout>2222222>res22DDD>WATING-res11
异步的实现方式
- 回调函数
- Promise
- Generator
- async/awit (是Generator的语法糖)
回调函数
ajax('aaa', ()=>{
// callback 函数体
ajax('bbb', ()=>{
// callback 函数体
ajax('ccc', ()=>{
// callback 函数体
})
})
})
es6出现之前,利用回调函数解决异步执行问题。该方式的弊端一是没有顺序可言,嵌套函数带来的调试困难,不利于维护与阅读;而是耦合性太高,某一个嵌套出问题会影响整个回调;会出现回调地狱的问题。
Promise
let a = new Promise(function(resolve, reject){
_this.$http("GET", '/import/get_deal_speed.php?id='+id).then(
res => {
if(res.errorCode){
reject(res)
}else{
resolve(res)
}
})
})
return a
}
通过promise处理异步,代码更加清晰,解决了回调地狱的问题,Promise的then()的链式调用更让人接受。
Promise的三个状态pending resolved rejected
Promise也存在缺点:①Promise的内部错误使用try catch捕捉不到,只能用then的第二个回调或catch来捕获 ②是Promise一旦新建就会立即执行,无法取消
let pro
try{ pro = new Promise((resolve,reject) => {
throw Error('err....') })
}catch(err){
console.log('catch',err) // 不会打印
}
pro.catch(err=>{ console.log('promise',err) // 会打印 })
async/await
async function fetch() {
await ajax('aaa')
await ajax('bbb')
await ajax('ccc')
}
async的返回值是Promise对象,可以调用then()方法
async function fn() { return 'async' }
fn().then(res => { console.log(res) // 'async' })
promise和async/await的区别
Promise 通过使用 then() 和 catch() 方法来处理异步操作的结果,而 async/await 通过使用 async 和 await 关键字来等待异步操作的结果。async/await 是建立在 Promise 之上的语法糖,它使得异步代码更加易读、易写,同时也使得代码结构更加清晰、易于维护。在实际开发中,我们可以根据需要选择合适的异步编程方式来处理异步操作。
- async/await使异步函数内部是同步执行的,简化回调,直接返回一个promise对象
promise.all()和async/await有什么区别
- promise.all(c(),b(),a())没有执行顺序
- async 和awiat搭配使用让方法同步执行方法就是按照程序的书写顺序依次执行的
function A(){
return new Promise(
(resolve,reject)=>{
setTimeout(() => {
console.log('aaaaaaaaa')
resolve('A'+new Date())
}, 3000);
}
)
}
function B(){
return new Promise(
(resolve,reject)=>{
setTimeout(() => {
console.log('bbbbbbbbb')
resolve('B'+new Date())
}, 4000);
}
)
}
function C(){
return new Promise(
(resolve,reject)=>{
setTimeout(() => {
console.log('ccccccccc')
resolve('C'+new Date())
}, 5000);
}
)
}
Promise.all([C(),B(),A()]).then(res=>{console.log(res)})
// 打印 aaaaa>bbbbb>cccc 调用的顺序是C>B>A
async function D(){
var c = await C();
var b = await B();
var a = await A();
console.log(a)
console.log(b)
console.log(c)
}
D();
//打印 cccccccc>bbbbbbb>aaaaaaaaa 按调用顺序执行
Proxy
什么是代理:可以定义对象的各种操作的自定义行为
- Proxy(target,handles)
let target = {};
let handles = {};
let proxy = new Proxy(target,handles)
proxy.a = 123;
console.log(target.a) //123
封装 继承 多态
面向对象的三大特点是: 封装 继承 多态
什么是面向对象: 对象是一组无序的相关属性和方法的集合。
优点是:易维护,易复用,易扩展,封装继承多态的特性可以设计出低耦合的系统,使系统更加灵活。
缺点是:性能比面向过程低(c语言)
封装
什么是封装: 创建一个对象结构,集中保存一个事物的属性和方法
如何创建对象:①对象字面量,用{}来创建
var obj = {
name: 'xiaoming';
age: 19;
dosometing:function(){}
}
②通过new实例化类创建
var obj = new Object();
obj.name = 'xiaoming';
obj.age = 18;
obj.intr = function(){console.log('sss')}
继承
什么是继承: 父对象中的属性和方法,子对象无需重复创建,可以直接调用
js中的继承是通过原型对象来实现的。新建一个构造函数时,会自动生成一个空的原型对象; 构造函数.prototype ->原型对象 ;实例对象.proto -> 构造函数的原型对象
多态
什么是多态:就是一个函数在不同情况下表现的不同状态
多态包括重写和重载
- 重写是在子对象中定义已经存在在父对象中的属性,使用时优先使用自定义的。
- 重载是相同的函数名,不同形参列表的函数,在调用时,可以根据传入的参数不同,自动匹配函数来调用。js中不支持重载,如果存在多个同名函数,最后一个函数会覆盖前面的函数,可以利用arguments实现
ES6的class中的静态属性和方法 static关键字
类:类用于创建对象的模板。用代码封装处理该数据。 类实际上是特殊的函数。类语法分为类声明和类表达式。
- 类声明
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
}
类声明和函数声明的的区别在于类声明不会提升。首先要声明类,才能访问它,否则会报错。
- 类表达式:类表达式是定义类的另一种方法。类表达式可以命名或不命名。
// 未命名/匿名类
let Rectangle = class {
constructor(height, width) {
this.height = height;
this.width = width;
}
};
console.log(Rectangle.name);
// output: "Rectangle"
// 命名类
let Rectangle = class Rectangle2 {
constructor(height, width) {
this.height = height;
this.width = width;
}
};
console.log(Rectangle.name);
// 输出:"Rectangle2"
- 类的原型方法
class Rectangle {
// constructor
constructor(height, width) {
this.height = height;
this.width = width;
}
// Getter
get area() {
return this.calcArea()
}
// Method
calcArea() {
return this.height * this.width;
}
}
const square = new Rectangle(10, 10);
console.log(square.area);
// 100
- 类的静态方法
static 关键字用来定义一个类的一个静态方法。调用静态方法不需要实例化 (en-US) 该类,但不能通过一个类实例调用静态方法。静态方法通常用于为一个应用程序创建工具函数。
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
static displayName = "Point";
static distance(a, b) {
const dx = a.x - b.x;
const dy = a.y - b.y;
return Math.hypot(dx, dy);
}
}
const p1 = new Point(5, 5);
const p2 = new Point(10,10);
p1.displayName;
// undefined
p1.distance;
// undefined
console.log(Point.displayName);
// "Point"
console.log(Point.distance(p1, p2));
// 7.0710678118654755
- 调用类的静态方法,需要使用 类本身调用
- 在类里面的,静态方法中调用其他静态方法,需要使用
this关键字
class StaticMethodCall {
static staticMethod() {
return 'Static method has been called';
}
static anotherStaticMethod() {
return this.staticMethod() + ' from another static method';
}
}
console.log(StaticMethodCall.staticMethod())
// 'Static method has been called'
console.log(StaticMethodCall.anotherStaticMethod())
// 'Static method has been called from another static method'
- 非静态方法中,不能使用
this关键字来访问静态方法 (通过类名来调用;通过this.constructor来调用)
class StaticMethodCall {
constructor() {
console.log(StaticMethodCall.staticMethod());
// 'static method has been called.'
console.log(this.constructor.staticMethod());
// 'static method has been called.'
}
static staticMethod() {
return 'static method has been called.';
}
}
this的作用域
.call(null)
function a() {
console.log(this);
}
a.call(null); //打印window对象
根据ECMAScript262规范规定:如果第一个参数传入的对象调用者是null或者undefined,call方法将把全局对象(浏览器上是window对象)作为this的值。所以,不管传入null 还是 undefined,其this都是全局对象window。所以,在浏览器上答案是输出 window 对象。
'use strict';
function a() {
console.log(this);
}
a.call(null); // null
a.call(undefined); // undefined
在严格模式中,null 就是 null,undefined 就是 undefined
使用new构造函数
var obj = {
name : 'cuggz',
fun : function(){
console.log(this.name);
}
}
obj.fun() // cuggz
new obj.fun() // undefined
使用new构造函数时,其this指向的是全局环境window。
调用函数
var a = 1;
function printA(){
console.log(this.a);
}
var obj={
a:2,
foo:printA,
bar:function(){
printA();
}
}
obj.foo(); // 2
obj.bar(); // 1
var foo = obj.foo;
foo(); // 1
解析:
- obj.foo(),foo 的this指向obj对象,所以a会输出2;
- obj.bar(),printA在bar方法中执行,所以此时printA的this指向的是window,所以会输出1;
- foo(),foo是在全局对象中执行的,所以其this指向的是window,所以会输出1;
匿名函数
var x = 3;
var y = 4;
var obj = {
x: 1,
y: 6,
getX: function() {
var x = 5;
return function() {
return this.x;
}();
},
getY: function() {
var y = 7;
return this.y;
}
}
console.log(obj.getX()) // 3
console.log(obj.getY()) // 6
解析:
- 我们知道,匿名函数的this是指向全局对象的,所以this指向window,会打印出3;
- getY是由obj调用的,所以其this指向的是obj对象,会打印出6。