在JS中面向类/实例进行程序设计,就是经典的面向对象编程javascript
- 类和实例的建立(构造函数模式)
- prototype / _proto_ (原型和原型链)
- 函数的三种角色
- 基于内置类原型扩展方法,实现链式写法
- 借用内置类原型上的方法,实现一些特殊的需求(例如:把类数组转换为数组)
- 细小的知识点:instanceof / constructor / hasOwnProperty ....
- 类的继承封装和多态
- ......
构造函数java
function Fn(n,m){
let plus=n+m,
minus=n-m;
this.x=plus;
this.y=minus;
this.print=function(){
console.log(this.x+this.y);
}
}
let f1=new Fn(30,10);
f1.print();
console.log(f1.plus); //=>undefined
console.log(f1 instanceof Fn); //=>true
let f2=new Fn; //=>没有传递任何的实参
console.log(f1.print===f2.print); //=>false
/* * 构造函数执行 * 1.开辟一个新的私有做用域 * 2.形参赋值&变量提高 * 3.浏览器在当前做用域中建立一个实例对象@A,而且让THIS指向它 * 4.代码执行 this=>当前类的实例@A * this.xxx=xxx都是给当前实例@A设置的私有属性 * 除此以外的私有变量等和@A这个实例没有必然的关系 * 5.即便咱们不设置RETURN,浏览器也会默认把实例@A返回,而外面的f1/f2接收到的就是返回的实例,因此也说f1/f2是Fn这个类的实例(若是手动返回的是引用类型值,会以用户返回的为主,也就是返回的再也不是Fn的实例,若是返回基本类型值,对原有的操做无影响) */
Fn(10,20);
window.print();
/* * 普通函数执行 * 1.造成私有的栈内存(私有的做用域scope) * 2.形参赋值&变量提高 n=10 m=20 * 3.代码执行 this=>window * 4.没有RETURN返回值 */
复制代码
原型:prototype 和 原型链:_proto_面试
function Fn() {
this.x = 100;
this.y = 200;
this.getX = function () {
console.log(this.x);
}
}
Fn.prototype.getX = function () {
console.log(this.x);
};
Fn.prototype.getY = function () {
console.log(this.y);
};
let f1 = new Fn;
let f2 = new Fn;
console.log(f1.getX === f2.getX);
console.log(f1.getY === f2.getY);
console.log(f1.__proto__.getY === Fn.prototype.getY);
console.log(f1.__proto__.getX === f2.getX);
console.log(f1.getX === Fn.prototype.getX);
console.log(f1.constructor);
console.log(Fn.prototype.__proto__.constructor);
f1.getX(); //=>this:f1 =>console.log(f1.x) =>100
f1.__proto__.getX(); //=>this:f1.__proto__ =>console.log(f1.__proto__.x) =>undefined
f2.getY(); //=>this:f2
Fn.prototype.getY(); //=>this:Fn.prototype
复制代码
函数三种角色编程
三种角色之间没有必然的联系数组
//=>JS中运算符的优先级:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Operator_Precedence
function Foo() {
getName = function () {
console.log(1);
};
return this;
}
Foo.getName = function () {
console.log(2);
};
Foo.prototype.getName = function () {
console.log(3);
};
var getName = function () {
console.log(4);
};
function getName() {
console.log(5);
}
Foo.getName();
getName();
Foo().getName();
getName();
new Foo.getName();
new Foo().getName();
new new Foo().getName();
复制代码
基于内置类原型扩展方法,实现链式写法浏览器
let ary = [12,13,13,12,24,13,12];
//=>ary是Array数组类的实例,因此能够调取Array.prototype上的方法,sort方法中的this是ary,当前要排序的数组实例(底层理解:sort是内置方法,它能够实现排序,ary.sort(...)本意:ary先基于__proto__找到Array.prototype上的sort方法,而且把sort方法执行,方法中的this是ary,sort方法在执行的时候,会把this对应的数组进行排序处理)
ary.sort((a,b)=>a-b);
/* //=>slice执行的时候,方法中的this是谁,就至关于把谁克隆成一份全新的数组出来 Array.prototype.slice=function(){ //this:当前须要操做的这个数组 let newAry=[]; for(let i=0;i<this.length;i++){ newAry.push(this[i]); } return newAry; }; */
let newAry = ary.slice(0);
newAry = Array.prototype.slice.call(ary,0);
function fn(){
//=>arguments类数组集合(实参集合):不是Array的实例,它就是一个对象而已,不能直接使用数组中的方法 (=>把类数组转换为数组)
let ary = Array.prototype.slice.call(arguments,0);
ary = [].slice.call(arguments,0);
//=>借用数组原型上的FOREACH方法,实现给类数组进行循环(内置方法中的THIS是谁,其实当前方法就在操做谁)
[].forEach.call(arguments,item=>{
});
}
fn(10,20,30,40,50);
复制代码
内置方法不少,可是不必定彻底够用 ,不少时候咱们须要本身向内置类的原型上扩展方法来实现一些需求闭包
~function(){
function unique(){
//注意:this是谁就给谁去重
let temp={};
for(let i=0;i<this.length;i++){
let item=this[i];
if(typeof temp[item]!=="undefined"){
//this.splice(i,1);
this[i]=this[this.length-1];
this.length--;
i--;
continue;
}
temp[item]=item;
}
temp=null;
//注意:返回的结果若是仍是数组,则继续能够调取Array.prototype上的其它方法,实现“链式写法”
return this;
}
//往内置类原型上扩展方法:为了防止不修改原有内置的方法,咱们扩展的方法名要增长对应的前缀
Array.prototype.myUnique = unique;
}();
let ary = [12,13,13,12,24,13,12];
ary.myUnique().reverse().push('A');
//Array.prototype.myUnique.call(ary);
复制代码
THIS函数执行的主体:谁执行的app
THIS是谁和函数在哪执行和在哪定义都不要紧,想要分清执行主体,记住如下规律便可函数
function fn(n,m){
this.total=n+m;
}
let obj={name:'OBJ'};
fn(10,20); //=>this:window
//obj.fn(20,30); //=>报错:obj中没有fn属性
document.body.onclick=fn; //=>点击后FN中的this:BODY
document.body.onclick=function(){
//=>this:BODY
fn(30,40); //=>this:window
};
fn.call(); //=>this:window 不传或者传递null/undefined都是
fn.call(obj,10,20); //=>this:obj
fn.apply(obj,[10,20]);//=>this:obj APPLY要求传递的参数是数组
document.body.onclick=fn.bind(obj,10,20); //=>BIND是预处理THIS,此时的FN尚未执行,只是把THIS改为了OBJ,点击BODY的时候才执行的 =>“柯理化函数”(预处理机制)
复制代码
构造函数中的THISui
function Fn(){
this.x=100;
this.y=200;
}
Fn.prototype.sum=function(){
console.log(this.x+this.y);
};
let f = new Fn; //=>Fn中的this:f 当前类的实例
f.sum(); //=>this:f
Fn.prototype.sum(); //=>this:Fn.prototype
f.__proto__.sum(); //=>this:f.__proto__
复制代码
//=>ES6的写法
class Fn{
constructor(){
//=>this:当前Fn类的实例
this.x=100;
this.y=200;
}
//=>直接写的方法就是放到原型上的
sum(){console.log(this.x+this.y);}
//=>STATIC修饰的都是把Fn当作普通对象设置的键值对
static unique(){}
}
Fn.prototype.name='珠峰';
Fn.age=10;
let f = new Fn;
f.sum();
Fn.unique();
//Fn();//=>Uncaught TypeError: Class constructor Fn cannot be invoked without 'new'
复制代码
箭头函数中的THIS
window.name='WINDOW';
let obj={
name:'OBJ',
fn:()=>{
console.log(this.name);
}
};
obj.fn();//=>this:window
obj.fn.call(obj);//=>this:window
//==================
document.body.onclick=function(){
//=>this:BODY
let _this=this;
_this.count=0;
/*setTimeout(function(){ //=>this:window _this.count++; },1000);*/
setTimeout(()=>{
//=>this:没有本身的THIS,继承下上文中的,也就是BODY
this.count++;
},1000);
}
复制代码
关于THIS这块的问题: