理解new运算符
- new运算符的工作流程:
- 1.创建一个空对象(
var object={}); - 2.使得该空对象继承于通过
new运算符构造的函数的原型(object.__proto__=Fn.prototype) - 3.使用指定的参数调用构造函数Fn,并将this绑定到实例对象上。(
Fn.call(object,a,b,c)) - 4.由构造函数返回的对象就是
new表达式的结果(没有参数时不加括号也可以执行),如果构造函数没有显式的返回一个对象,自动返回实例化对象并与之绑定this,如果用户主动返回对象,会覆盖正常的创建对象步骤。
//构造函数没有显式的返回对象
function Fn(){
this.num=10;
}
//会发生的执行顺序
var object={}
object.__proto__=Fn.prototype;
Fn.call(object)
--->{num:10}
//构造函数显式的返回对象
function Fn(){
this.num=10;
return {num:2}
}
//会发生的执行顺序
var object={}
object.__proto__=Fn.prototype;
Fn.apply(object)
--->{num:2}
构造函数
- 理解什么是私有成员?
- 理解什么是静态属性?
- 理解什么是静态方法?
- 理解通过call来继承基类的缺点?
- 理解new运算符创建的实例化对象是不是互不干扰的?
function Dad(){
//私有成员-只有公有方法才能调取
let _weight='80kg';
this.age=20
};
//静态属性-只能通过类来调用,实例化对象是调用不了的
Dad.num=10;
//静态方法-只能通过类来调用,实例化对象是调用不了的
Dad.getNum=function(){cosole.log('静态方法')}
Dad.prototype.add=function(){console.log(this.age)};
//只使用Dad.call(this)只能继承父类的实例化对象上的属性和方法
//继承不到原型链上的属性方法
function Son(){Dad.call(this)};
//精髓之句
Son.prototype=new Dad();
//每次通过new运算符创建的实例化对象都是互不干扰的
var xx=new Son();
console.log('xx',xx.age); //'xx' 20
var xxv=new Son();
xxv.age=22;
console.log('xxv',xxv.age) //'xxv' 22
xx.add() //20
xxv.add() //22
console.log(xx) //如下图的结构
ES6类的运用
- 理解es6对私有属性的实现方式?
- 理解es6静态属性的写法?
- 理解es6怎么将方法放在构造函数原型上?
- 理解es6是通过什么来实现继承的和好处?
- 构造函数对私有属性的实现方式
- es6类对私有属性的实现方式
console.log(typeOf Coder) //'function'
//函数是一等对象
class Coder{
//私有属性只有在类内部能被调用。
//es6将私有属性放在了实例化对象上
//但是想要通过实例化对象.私有属性的方式获取是拿不到的
#weight='80kg'
//这种是通过公有方法调取私有属性
getWeight(){
return this.#weight
}
//与类相关性强的属性并且和实例化对象的相关性不强的属性可以考虑使用静态属性像Math.Pi、Promise.resolve……
//静态属性是通过类直接调用的
//实例化对象是调用不到静态属性的
//es5写法为Coder.name=function(){}
static name(val){
console.log(val);
return val;
}
//es5写法为Coder.num=10。属于静态属性。只能通过类调用
static num=10;
//省略static后会自动在实例化对象上添加this.age=10;
age=10;
constructor(a,b){
this.a=a;
this.b=b;
this.fn=function(){
console.log('实例对象身上的方法')
}
}
//自动被添加到了Coder.prototype上
add(){
return this.a+this.b;
}
//自动被添加到了Coder.prototype上
hobby(){
console.log('我在原型上')
}
}
let shuang=new Coder(1,2);
console.log(shuang);
//原型上的属性
Coder.prototype.shuxing=110
Coder.num //10
构造函数的继承
function Dad(height){
this.name='zhangsan';
this.height=height
}
function Son(height){
Dad.call(this,height)
}
let zhangsan=new Son('178')
console.log(zhangsan)
ES6类的继承
class Dad{
static num=10;
constructor(height){
this.name='张三';
this.height=hright
}
}
//解决了原型继承引用的问题
class Son extends Dad{
constructor(){
//必须写在实例化对象其他的属性方法之前
//通过super把父类的constructor和子类的constructor联系了起来
super(height);
this.weight='80kg'
}
}
console.log(Son.num) //10
利用ES6类和ES6模块化构造出数据的案例
-
需求:构造一个管理类,该管理类包含一个登录方法(原型上的方法:在玩家登录后创建一个玩家类)和一个玩家类。
-
该玩家类具有玩家名字(实例化对象上的属性)和一个英雄类。
-
该英雄类有英雄名字、图标(实例化对象上的属性)和一个技能类和一个皮肤类
-
终极目标数据如下
-
在还没有执行登录方法时的管理类数据是
-
利用es6类构造出Game
//需要在这引入玩家类player
import Player from './player.js';
export default class Game{
constructor(){
this.player = null;
}
login(name){
this.player = new Player(name);
}
}
- 利用es6类构造出玩家类
//需要在这引入英雄类
import Yase from './heroes/yase.js';
import Luban from './heroes/luban.js';
export default class Player{
constructor(name){
this.name = name;
this.heroes = [new Yase(),new Luban()];
}
}
- 利用es6类构造出英雄类(这里以luban类为例)
//同理需要在这里以es6模块化的方式引入技能类和皮肤类
export default class luban{
constructor(){
this.name='鲁班1';
this.ico='./sources/heros/luban1.png'
this.skills=[new S1(),new S2(),new S3()];
this.skins=[new K1(),new K2()]
}
}
- 利用es6类构造出技能类
export default class S1{
constructor(){
this.name='技能一';
this.ico='./sources/skills/11210.png'
}
}
- 英雄的基类模板
// 所有英雄的共性特征
export default class Hero{
constructor({name,ico,skills,skins}){
this.name = name;
this.ico = ico;
this.skills = skills;
this.skins = skins;
}
}
- 让yase继承英雄的基类模板(这里只以yase为例)
//引入依赖模块后(省略引入依赖模块)
export default class Yase extends Hero{
constructor(){
let opts = {
name:"亚瑟1",
ico:"./sources/heros/yase1.png",
skills:[new S1(),new S2(),new S3()],
skins:[new Skin1,new Skin2,new Skin3]
}
super(opts);
// this.name = "亚瑟1";
// this.ico = "./sources/heros/yase1.png";
// this.skills = [new S1(),new S2(),new S3()];
// this.skins = [new Skin1,new Skin2,new Skin3];
}
}
- 利用es6类构造出皮肤类
export default class K1{
constructor(){
this.name='皮肤一'
this.ico='./sources/skins/301120.png'
}
}
拓展学习
-
localStorage的实际应用场景
-
1.存储数组格式的内容
//右边传值虽然是数组格式但是会自动转为字符串格式
localStorage.setItem('arrToString',[1,1,1,0]);
localStorage.getItem('arrToString')
//"1,1,1,0"
//如果想要在恢复成数组格式可以使用split
localStorage.getItem('arrToString').split(',')
//["1", "1", "1", "0"]
- 2.存储对象格式的内容
localStorage.setItem('objToString',{a:0,b:1});
localStorage.getItem('objToString');
//"[object Object]"
localStorage.setItem('objToString',JSON.stringify({a:0,b:1}));
JSON.parse(localStorage.getItem('objToString'));
//{a: 0, b: 1}
- 3.在不支持
localStorage的浏览器下可以考虑使用cookie
//存储,IE6~7 cookie 其他浏览器HTML5本地存储
if (window.localStorage) {
localStorage.setItem("data", data);
} else {
Cookie.write("data", data);
}
var getStore = window.localStorage? localStorage.getItem("data"): Cookie.read("data");
-
合并空运算符(??)
-
1.空值合并操作符
(??)是一个逻辑操作符,当左侧的操作数为null或者undefined时,返回其右侧操作数,否则返回左侧操作数。
const b = 0 || 42;
console.log(b); //42
const b = 0 ?? 42;
console.log(b); //0
- 2.由于
||是一个布尔逻辑运算符,左侧的操作数会被强制转换成布尔值用于求值。任何假值(0, '', NaN, null, undefined)都不会被返回。这导致如果你使用0,''或NaN作为有效值,就会出现不可预料的后果。
let count = 0;
let text = "";
let qty = count || 42;
let message = text || "opps!";
console.log(qty); // 42,而不是 0
console.log(message); // "opps!",而不是 ""
let cqty= 0 ?? 42
console.log(cqty) //0
let cmessage='' ?? 'opps';
console.log(cmessage) //''
function A() { console.log('函数 A 被调用了'); return undefined; }
function B() { console.log('函数 B 被调用了'); return false; }
function C() { console.log('函数 C 被调用了'); return "foo"; }
console.log( A() ?? C() );
// 依次打印 "函数 A 被调用了"、"函数 C 被调用了"、"foo"。
//因为A() 返回了 undefined,所以操作符两边的表达式都被执行了
console.log( B() ?? C() );
// 依次打印 "函数 B 被调用了"、"false"。
//因为B() 返回了 false(既不是 null 也不是 undefined)
//所以右侧表达式没有被执行
-
可选链式操作符
-
可选链操作符( ?. )允许读取位于连接对象链深处的属性的值,而不必明确验证链中的每个引用是否有效。
-
?. 操作符的功能类似于 . 链式操作符,不同之处在于,在引用为空(nullish ) (null 或者 undefined) 的情况下不会引起错误,该表达式短路返回值是 undefined。
-
与函数调用一起使用时,如果给定的函数不存在,则返回 undefined。
let obj={}
console.log(obj.mayname.age) //报错
console.log(obj?.mayname.age) //undefined
console.log(obj?.mayname?.age) //undefined
- 一个存在嵌套结构的对象 obj。不使用可选链的话,查找一个深度嵌套的子属性时,需要验证之间的引用,例如
let nestedProp = obj.first && obj.first.second;这句话的意思是先去验证obj.first的值是不是null或者undefined,如果不是在去访问obj.first.second
let temp = obj.first;
let nestedProp = ((temp === null || temp === undefined) ? undefined : temp.second);
- 当尝试调用一个可能不存在的方法时也可以使用可选链。
函数调用时如果被调用的方法不存在,使用可选链可以使表达式自动返回undefined而不是抛出一个异常。
let result = someInterface.customMethod?.();
const adventurer = {
name: 'Alice',
cat: {
name: 'Dinah'
},
someNonExistentMethod(){
console.log('1')
}
};
const dogName = adventurer.dog?.name;
console.log(dogName);
// expected output: undefined
console.log(adventurer.someNonExistentMethod?.());
// expected output: undefined
- 当在表达式中使用可选链时,如果左操作数是
null或undefined,表达式将不会被计算,例如:
let p = null;
let x = 0;
let prop = p?.[x++];
console.log(x); // x 将不会被递增,依旧输出 0
- 空值合并操作符可以在使用可选链时设置一个默认值:
let customer = {
name: "Carl",
details: { age: 82 }
};
let str='前面是null或者undefined我就发挥作用了'
let customer = (customer?.city) ?? str;
console.log(customer); // “前面是null或者undefined我就发挥作用了”
-
不借助node.js环境下实现es6模块化
-
浏览器默认模块化在
script标签里加入"type=module"; -
导出的关键字为
export。export可以导出多个,export default只能导出一个;
1.export { a ,b , c};
//利用关键字as
2.export { a as aa ,b , c};
3.export let c = ()=>{console.log("I am function...")};
4.export default a;
- 导入方式:关键字
import。
1.要和`export`导出的命名**保持一致**。有加括号
import {a , b , c} from './module.js';
2.没加括号。命名可以自定义
import obj from './module.js';
3.通配符 "*"方式导入
import * as obj from './moduleb.js';
4.按需求延迟导入(模块默认一加载页面就给你导入了)
document.onclick =async function(){
// import {fn1} from './fn.js';
let res = await import("./fn.js");
console.log(res);
}