面向对象基础知识概括
=============数据属性和访问器属性
数据属性 和 访问器属性
get 获取 Own 自己的property 属性 Descriptor 描述符
value:zhangsan //该key对应的值
writable : true //可修改
enumerable: true //可枚举性 是否可被遍历
configurable:true //可配置性 能否被删除
默认都是可修改,可被遍历到,可被删除
define 定义 property 属性
Object.definProperty(obj,key,Descriptor);默认不可被修改,遍历,删除
const obj = { name : "zhangsan", age : 18 } Object.defineProperty(obj,'age',{ value : 18, writable:true, // 默认为false 是否可修改 enumerable : false, // 默认为false 是否可遍历 configurable: true //默认为false 是否可删除 }) obj.age = 20; for(let key in obj){ console.log(key) //name,age } console.log(obj); delete obj.age; console.log(obj) //{name: "zhangsan"}
存取描述符
两个方法 get() set()
存取描述符不可与数据描述符同时存在,即get set 不可与 value writable 同时存在
案例:输入属性值,进行判断
const obj = { name : "zhangsan", // 如果这里没加下划线,那么下面get访问也不用加 // _age : 38, _score: 60 } Object.defineProperty(obj,'score',{ //这里的参数score必须对应下面的set,包括下划线 // value : 18, // writable:false, // 默认为false 是否可修改 enumerable : false, // 默认为false 是否可遍历 configurable: false, //默认为false 是否可删除 get() { //访问对象属性时自动被调用 return this._score; //获取obj.score的值 }, //输入的值 set(value){ //设置对象属性时自动被调用 //判断值 if(value < 0 || value > 100){ console.log("请输入正确分数"); }else{ this._score = value; //输入的值赋值给obj.score (注:这里不能使用下划线) console.log("录入成功"); return value;// 返回输入的值 } } })
===========面向对象
面向对象编程,一种编程范式(编程风格,编程方式)。
将现实问题构建关系,抽象成类,为类定义属性,方法。再将类【实例化】成【实例】,访问实例属性,调用实例方法进行使用。
实例化:新增 新建 创建
例如:张三想要个女儿,生的那个女儿就是实例。
Arr let arr = new Array();
编程范式
1.声明式编程 HTML,CSS告诉计算机需要XX
2.命令式编程 每一步都需要告诉计算机(比如声明一个变量),强调了做事的步骤
1.面向过程 分析出解决问题的每一步,逐步实现
2.面向对象 把构成问题的事物分解成各个对象,让具体对象去实现相应事情
ATM
类
种类,模板,模具
数量庞大就可以用类区分,归纳
类属于对象,对象有这些类
类是对象的一种抽象概括,对象是类的具体实现,类是模板,用于生成多个实例。
封装:封装,继承,多态
封装:信息隐藏,把实现功能的方法包装起来。函数是对于算法的封装,类是对于属性和方法的封装
函数的三要素,随机数函数, 餐馆吃饭(只关心菜品,不用关心厨师如何做出来)。
继承:子类可以继承父类的所有属性和方法,还可以扩展自己的属性和方法。
=========封装
1.对象的方式表现出来,看不出实例和类的关联
把属性和方法组成类的过程就叫做封装
//cat 想要表示猫的类 const cat = { //cat的属性 name : '', age : '', gender : '', color : '' }; const cat1 ={ name : 'xiaohei', age : '1', gender : 'male', color : 'block' } const cat1 ={ name : 'xiaohuang', age : '2', gender : 'female', color : 'yellow' }
2.ES6 class 声明类
关键字class
//此处的Dog实际是一个构造函数 class Dog { constructor(name,age,gender){ this.name = name; this.age = age; this.gender = gender; } } let xiaohei = new Dog('xiaohei',2,'male'); //xiaohei实例化出来的具体某个对象 console.log(xiaohei); //案例:我叫张三,我的小狗叫小黑 class Person{ constructor(name){ this.name = name; console.log(this);// Person {name: "张三"} } talk(){ console.log(我叫${this.name},我的小狗叫${xiaohei.name}); } } let zhangsan = new Person("张三") zhangsan.talk(); // 我叫张三,我的小狗叫xiaohei
==========工厂模式
` //工厂模式
function CatFactory(name,age,gender){
return {
name : name,
age : age,
gender : gender
}
}
let cat_obj = CatFactory('xiaohuang','1','female');
console.log(cat_obj);
function Dogfactory(name,age,gender,type){
return {
name,
age,
gender,
type
}
}
let dog1_obj = Dogfactory('小白',2,'female','金毛'),
dog2_obj = Dogfactory('小黄',3,'male','拉布拉多');
console.log(dog1_obj,dog2_obj);`
========混合模式(构造函数+原型prototype)
将实例对象共享的方法,放在类原型上,实现方法共享,节省内存
function Cat(name,age,gender){ //用this,不能用return this.name = name; this.age = age; this.gender = gender; } Cat.prototype.eat = function(){ console.log('饿了,干饭去'); } let Cat1 = new Cat('xiaohuang',2,'female'), Cat2 = new Cat('xiaobai',2,'male'); Cat1.eat(); Cat2.eat();
======静态方法(工具型) 实例方法
let arr = new Array(); arr.push(); //得到实例对象后通过实例对象去调用的方法,属性 Math.random(); Json.parse() //直接在类,构造函数身上使用的
======使用new关键字在内部经历的四个步骤
1.创建了一个该类型的空对象, 就像是 let _obj = {};
2.将这个新对象的_proto_(隐式原型)指向了类(构造函数)的prototype(原型)就像是 cat1._proto_ =>Cat.prototype
3.将构造函数(类)内部的this 指向该实例对象,并执行代码 就像是 this => _obj
4.返回该对象
=======原型
每一个函数都有一个属性,prototype表示原型(原型对象)。
class Dog{} new Dog()
原型的作用:
给所有实例提供公共的访问。
`let arr = [];
console.log(Array.prototype);
const obj1 = {
name : "zhangsan"
}
console.log(obj1.name,obj1.age);// zhangsan un
Object.prototype.age = 18;
console.log(obj1.name,obj1.age);//zhangsan 18`
2.对象和隐式原型
每一个实例对象都有一个_proto_,称为隐式原型,指向创建该对象的类的原型。
const arr1 = []; //new Array()
console.log(arr1.proto === Array.prototype);//true
3.原型和构造器
每一个类(函数)的prototype上都有constructor指向函数本身
每个原型都有一个constructor属性,指向该关联的构造函数。
console.log(Array.prototype.constructor === Array);//true
4.原型链
链 链式调用 快 美化代码 简洁方便
作用域链 找变量
原型链 找公共属性 方法是特殊的属性,一个实例在调用属性,方法时,会以此从实例自身 -> 创建该实例的构造函数的原型 -> Object的原型。
查看是否有对应的属性或方法。专业上称为原型链。
function Pig(){}; let pi_obj = new Pig(); console.log(pi_obj); Pig.prototype.eat = function(){ console.log(666); } pi_obj.eat();//666 pi_obj.toString();
instanceof 判断 该对象是不是该类(函数)的实例对象。
function Pp(){} let P_obj = new Pp(); console.log(P_obj instanceof Pp);//true let pp = {} console.log(pp instanceof Pp);//false
=========继承
当 类 和 类 之间发生了is关系,就看做继承。
继承的作用:子类继承父类,继承所有的属性和方法。子类可以扩展自己的属性和方法。实现代码复用。
糟糕的继承关系,出现代码冗余,关系臃肿。
class Animal{ //动物 constructor(type,name,age,gender){ this.type = type, this.name = name, this.age = age, this.gender = gender } eat(){ console.log("饿了,干饭去"); } } class Pig extends Animal{ constructor(type,name,age,gender,color){ super(type,name,age,gender); //替代了 this.xx = xx this.color = color; } } let cat_obj = new Animal('cat','xiaobai',2,'male'); cat_obj.eat(); console.log(cat_obj); let pig_obj = new Pig('pig','xiaohei',3,'female','red'); pig_obj.eat(); console.log(pig_obj);
========= this
1.全局环境下,this => windows。
2.普通函数(包括嵌套) this => windows。
3.通过对象调用的方法,this=>对象。
4.通过事件 this => 事件源。
5.构造函数 new 出来的实例,this => 实例。
6.箭头函数的this指向取决于当前箭头函数声明的环境(执行上下文 EC)。
箭头函数需要获取函数定义时所在的EC的this,箭头函数定义在哪个作用域中,就继承当前作用域的this的指向。
1.全局 this => window。
let foo =() => { console.log(this);//window }
EC 3类 :全局环境(全局EC),局部环境(局部EC,函数级别上下文),eval环境(eval EC);
const obj = { name : "zhangsan" } talk(){ console.log(this)//指向obj }
2.对象不会生成作用域。
let count = () => { console.log(this); console.log(this.name); } var number = 5; let obj = { say : count }; obj.say();
const zhangsan = { name : 'zhangsan', talk : function(){ console.log(this); //zhangsan对象 let lisi = { name : 'lisi', talk : () =>{ console.log(this); // zhangsan对象 (因为这个箭头函数没有this指向且在zhangsan.talk()的作用域中) } } lisi.talk(); } } zhangsan.talk();
let obj = { a : 10, fn :() =>{ let bar = () =>{ console.log(this);//window } bar(); } } obj.fn();
======修改 this 的指向
1.call 呼叫
语法:fn.call(target);
2.apply 应用
3.bind 捆绑
call,apply改变this的同时立即调用函数,bind 改变后返回一个新函数,需要重新调用。
function foo(){ console.log(this); } const obj = { name : "zhangsan" } foo() // window //对于传参:call 直接跟在target后, apply 以数组的方式 bind 调用函数的()里传 foo.call(obj,5); // zhangsan (foo的this的指向改为指向obj) foo.apply(obj,[5]); // zhangsan foo.bind(obj)(5); // zhangsan
var obj = { a:1, b : function(){ console.log(this.a); } } var a = 2; let fn = obj.b; obj.b(); //1 //指向obj对象 fn(); //2 //单纯的将函数赋值给fn,指向全局 obj.b.call(window)//2 //call(window) 全局变量a
=======作用域 scope
作用: 生效 域:区域 ,变量生效的范围,也叫词法环境。
作用域的产生时间:函数定义(声明)时产生。
作用域分类
3类
全局作用域(全局EC)
局部作用域(局部EC,函数级别上下文)
eval作用域(eval EC);
=======执行上下文 EC (execution context)
代码当前的执行环境,又称为执行上下文环境。
上下文 语文 css this
EC的产生时间,函数调用时产生
全局EC:默认环境。产生时间:打开页面---关闭页面 生命周期
node node 执行 ---- 关闭
局部(函数)EC,调用时----调用结束 生命周期
`=======执行上下文 EC (execution context)
代码当前的执行环境,又称为执行上下文环境。
上下文 语文 css this
EC的产生时间,函数调用时产生
全局EC:默认环境。产生时间:打开页面---关闭页面 生命周期
node node 执行 ---- 关闭
局部(函数)EC,调用时----调用结束 生命周期 `
=======EC的组成
当调用函数,新的EC会被创建,在JS引擎内部,这个EC的生命周期还会被分为:
1.创建阶段
2.激活阶段
3.销毁阶段
创建阶段
VO(variable object)变量对象:arguments,声明式函数,var声明的变量。
scope:确定作用域链
this:确定环境对象
激活阶段
函数的引用
变量赋值
执行其他代码
销毁阶段
(注:变量提升不提升值,即不赋值)!
VO是执行上下文创建阶段内部创建的一个对象,用于记录在当前作用域中所有可用的变量
VO中的属性都是不可访问的,只有等激活阶段VO变AO(active object)才能被访问。
function outer(){ console.log(a); //undefined 变量提升不赋值 console.log(inner()); //2 函数提升 var a = 1; function inner(){ return 2; } } outer();
var a = 10; fn(); function fn(){ //内部的var不会提升到外部 var b = 5; console.log(a); //un console.log(b); //5 var a = "abc"; }
var a = 1; function fn(){ console.log(a);//un a = 2; console.log(a); //2 var a = 3; // 这个var a 提升到函数内部最上层 console.log(a); //3 } console.log(a); //1 fn(); console.log(a); //1
var fn = function(){ console.log(a);// 函数a本身 var a = 1; console.log(a); //1 a(); //报错不是一个函数,因为此时a=1 function a(){} } fn();
var a = 1; function fn(){ a = 10; return a; function a(){} } console.log(fn());//10 console.log(a); //1
var a = 100; function foo(){ bar(a); //执行结束出栈 console.log(a); //100 } function bar(a){ // AO进栈 console.log(--a); //99 } foo();
function foo(num){ if(num > 3){ foo(--num);//依次执行,出栈不再执行 } console.log(num); //3,3,4 } foo(5)
======= 作用域链 scope chain SC
作用域链是一套 EC 的访问规则,由当前上下文和上层上下文中一系列VO组成。
访问规则:先访问自身EC,若找不到变量,则访问声明时所在的EC的变量,以此类推到全局。
var food = "巧克力味的粑粑"; function eat(){ console.log(吃${food}`);//声明在全局,就在全局里面找
}
function fn(){
var food = "粑粑味的巧克力";
eat();
}
fn();
var food = "巧克力味的粑粑";
function fn(){
var food = "粑粑味的巧克力";
function eat(){
console.log(`吃${food}`);
}
eat();
}
fn();`
let a1 = 1,a2 =2,a3 = 3; function foo(){ let a2 = 6; console.log(a1,a2,a3); //1,6,un var a3 = 9; //a3变量提升 a1 = 8; function bar(b){ a3 = 7; let a4 = 6; console.log(a1,a2,a3);//8,6,7 } bar(7); } foo(); console.log(a1,a2,a3);//8,2,3 console.log(a4);//报错
垃圾:无用的东西
对于计算机:多余的,无用的数据
存储数据,计算机都会开辟空间来存储组织,但无限开辟空间则会出现爆栈影响性能。
计算机会在适当的时候自动清理无用的数据,自动执行,并不可见,引擎中有一个后台的进程被称为“垃圾回收器”。
function foo(){ let a = 5; console.log(a);//5 被标记“进入环境” } foo(); console.log(a);//无法被访问 “离开环境”
回收方案:
1.标记清除
2.引用计数
const obj = { name : "zhangsan" } obj = null;//切断了引用,计数为0,回收
======= 闭包 closure
原本应该被清理的数据,由于还有引用(使用),导致该销毁的数据没有被销毁。
难以描述的概念,书籍,文档对于闭包的描述都不一样。
MDN:函数和对其周围状态的引用捆绑在一起构成闭包,闭包可以让你从函数内部访问外部函数作用域,每当函数被创建,就会在函数生成时生成闭包。
javaScript高级程序设计:闭包是指有权访问另一个作用域中变量函数。
广义:所有函数都会生成闭包,因为只要有函数,就有SC。
狭义:函数的嵌套,内部函数在访问外部函数作用域,内部函数在自身以外被调用。
function outer(){ let number = 5; return function(){ console.log(number); } } let result = outer(); result();//5
====== 深浅拷贝
深拷贝:都是针对复杂数据类型,改变原有对象里的值,新的对象的值不会改变。
浅拷贝:改变原有对象里的值,新的对象的值也会改变
1.for, for of ,... , concat, map, slice, filter,Object.assgin(newArr,arr);
2.for in, ... ,Objcet.assgin(newobj,obj),Object.keys()等价于for in,JSON,obj =>str,str => newObj
数组深拷贝
` let arr = [1,2,3];
let newArr = [];
// newArr = arr.slice(0,4); //使用数组截取方法
// let newArr = arr.map(item => { //使用map
// return item;
// })
// newArr = arr.filter(item =>{ //使用filter
// return item;
// })
// newArr = [...arr]; //使用扩展运算符
// Object.assign(newArr,arr); //使用Object.assgin()
// for(let item of arr){ //使用for of
// newArr.push(item);
// }
arr[0] = 'a';
console.log(arr);
console.log(newArr);对象深拷贝 : 新对象不会因为旧对象属性的更改而发生改变
let zsObj = {
name : "zhangsan",
age: 18
}
let new_obj = {};
// for(let key in zsObj){
// new_obj[key] = zsObj[key]; //使用for in 赋值
// console.log(zsObj,new_obj);
// }
// Object.assign(new_obj,zsObj); //使用Object.assgin()
// new_obj = {...zsObj}; //使用扩展运算符
// new_obj = JSON.stringify(zsObj); //使用JSON
// console.log(JSON.parse(new_obj));
zsObj.name = 'lisi';
console.log(zsObj);
console.log(new_obj);`
深拷贝考虑:
1.递归考虑爆栈。
2.对象方法是否要拷贝,正则,JSON能够保存的数据类型不包括函数,undefined,data。
3.业务范畴,不能脱离业务场景。
4.库,框架已经封装好了克隆的方法,直接用。
浙公网安备 33010602011771号