JS面向对象

面向对象基础

构造函数和普通函数执行区别

1.png

/*
	普通函数执行:
		+ 初始化作用域链
		+ 初始化this	window/undefined
		+ 初始化arg
		+ 形参赋值
		+ 变量提升
		+ 代码执行
			return 什么就是返回什么
	构造函数执行:
		@1 创建当前的实例对象
		+ 初始化作用域链
		+ 初始化this @2 this指向创建的实例对象
		+ 初始化arg
		+ 形参赋值
		+ 变量提升	
		+ 代码执行
			return 的值如果是原始类型值或者没写return 就返回 创建的实例对象
					   返回的如果是个对象就是对象
*/
function Fn(x, y) {
    let sum = 10;
    this.total = x + y;
    this.say = function () {
        console.log(`我计算的和是:${this.total}`);
    };
}
let res = Fn(10, 20); //普通函数执行
let f1 = new Fn(10, 20); //构造函数执行
let f2 = new Fn;
console.log(f1.sum);		//=>undefined
console.log(f1.total);		//=>30
console.log(f1.say === f2.say); 	//=>false
复制代码

梳理一下原型链

2.png

const fn = function fn(){}
let fn1 = new fn();
/*
	1.函数和对象都具有__proto__,prototype属性
	2.函数可以--->对象
	3.对象可以--->函数
*/
//函数可以--->对象
console.log(Function.__proto__.__proto__ === Object.prototype);	//=>true
//对象可以--->函数
console.log(Object.__proto__ === Function.prototype);	//=>true


/*
·	1.实例对象有__proto__但是没有prototype
	2.一般不选择@1 因为兼容性不好 不兼容IE IE没有__proto__
*/
//@1
console.log(fn1.__proto__ === fn.prototype);	//=>true
//@2
console.log(Object.getPrototypeOf(fn1) === fn.prototype); //=>true


/*
	1.函数Function是对象Object的实例
		Object instanceof Function //=>true
	2.对象Object是的函数Function实例
		Function instanceof Object //=>true
	3.函数Function是函数Function的实例
		Function instanceof Function //=>true
		Function.__proto__ === Function.prototype //=>true
			任何一个函数的__proto__都可以找到Function.prototype
			只有这样我们才能保证,所有的函数都可以调用call/applay/bind等方法
	4..对象Object是对象Object的实例
		Function instanceof Function //=>true
		console.log({}.__proto__ === Object.prototype) //=>true
			任何一个对象的__proto__都可以找到Object.prototype
			只有这样我们才能保证,所有的函数都可以调用Object.hasOwnProperty()等方法
    
*/
复制代码

image-20210509232454213.png

image-20210509232606483.png

检测某个属性是否为对象的“私有属性”

Object.hasOwnProperty() 可以这样=> fn1.hasOwnProperty(“say”)

/*
	使用Object工具方法  Object.hasOwnProperty() 获得私有属性
*/
let obj = {
    name:'yzl'
}
console.log(obj.hasOwnProperty("name"))	//=>true

/*----*/
const fn = function fn(){
    /* this.say = function say(){
        console.log(1)
    } */
}
//添加在原型对象
fn.prototype.say = function (){}
let fn1 = new fn();
console.log(fn1.hasOwnProperty("say"))	//=>false
/*name存在堆中{
	name:'fn',
    length:0,
    prototype:0x00,
    __proto__:Function.__proto__
}*/
console.log(fn1.hasOwnProperty("name"))	//=>name
复制代码

只想检测是不是他的一个属性「不论是私有还是公有」

in 检测的目标是一个对象(函数也是对象)找不到会根据原型链往下找

function Fn(x, y) {
    let sum = 10;
    this.total = x + y;
    this.say = function () {
        console.log(`我计算的和是:${this.total}`);
    };
}

let f1 = new Fn(10, 20);
console.log('say' in f1); //=>true
console.log('hasOwnProperty' in f1); //=>true
console.log('sum' in f1); //=>false
/*
	分析找不到就Fn.__proto__.__proto__ 就跑到了Object.prototype
*/
console.log('hasOwnProperty' in Fn); //=>true
复制代码

需求:检测这个属性是否为他的公有属性?

思路:跳过本身查找原型链

Object.getPrototypeOf(obj) 使用方法

​ 直接获取一个对象(实例)的原型对象相当于fn.prototype

/*
	为啥使用 Object.getPrototypeOf(obj) 
		原因 1.跳过本身实例对象通过原型链查找找到了就是公有对象
			 2.ie浏览器没有__proto__	
*/
const fn = function fn(){
    this.toString = function (){
        console.log(1);
    }
}

let fn1 = new fn()

const hasPubProperty = function hasPubProperty(obj,attr){
    // @1 let proto = fn1.__proto__; //这样写兼容性不好
    // @2 let proto = fn.prototype;
    let proto = Object.getPrototypeOf(obj);
    while(proto){
        if(proto.hasOwnProperty(attr)){
            return true
        }else{
            proto = Object.getPrototypeOf(proto);
        }
    }
    return false
}

console.log(hasPubProperty(fn1,"a"));
复制代码

如何检测一个对象是不是当前构造函数的实例

instanceof

/*
	检测fn1是不是fn构造函数new出来的
*/
const fn = function fn(){}
let fn1 = new fn();
console.log(fn1 instanceof fn);	//=>true
console.log(fn1 instanceof Object);	//=>true
console.log(fn1 instanceof Function);	//=>true

console.log(fn instanceof Function);	//=>true
console.log(fn instanceof Object);	//=>true
复制代码

手写new方法

/*
	手写new方法的思路	(手动把 构造函数 和 普通函数 的区别手写出来)
		@1 创建一个实例对象 __proto__ 要指向 fn.prototype 
		@2 手动更改fn函数的this指向创建的实例对象 这样在函数中操作的就是实例对象
		@3 return 返回判断
		 	+ 如果不写return 或者 return 为一个原始类型值就 返回实例对象
          	+ 如果返回的是一个对象就返回本身对象	
*/


/*-----*/
/*
	使用箭头函数会发生错误 因为箭头函数不存在prototype
	const Dog = () => {
    	this.name = name
	}
*/
function Dog(name) {
	this.name = name;
}

Dog.prototype.bark = function () {
    console.log('wangwang');
};
Dog.prototype.sayName = function () {
    console.log('my name is ' + this.name);
};

const _new = function _new(fn, ...args) {
    // 格式校验	首先不是一个函数就报错 报错信息我们就看看 new 1();报错什么
    if (typeof fn !== 'function') throw new console.error(`${fn} is not a constructor`);
    /*
        @1 函数名字不能为Symbol/BigInt
            new Symbol() 报错  =>  _new(Symbol)也要报错
            new BigInt() 报错  =>  _new(BigInt)也要报错
       	@2 函数必须要有prototype
            箭头函数没有prototype
                ()=>(){}
                let obj = {
                    a(){},	//没有prototype
                    b: function(){}
                }
            ES6快捷函数没有prototype

    */
 if (/^(symbol|Bolean)$/i.test(fn.name) || typeof fn.prototype === "undefined") 
        throw new TypeError(`${fn.name} is not a constructor!`)
        /*
            @1 区别一 创建一个实例对象
            Object.create的作用创建一个对象把对象的__proto__指向一个原型对象
            let obj = {};
            obj.__proto__ = fn.prototype
        */
        let obj = Object.create(fn.prototype);
        /*
            @2 区别二 更改this指向
            	存下fn 函数的返回值
        */
        let result = fn.call(obj, ...args);
        /*
            @3 区别三 
                + 如果不写return 或者 return 为一个原始类型值就 返回实例对象
                + 如果返回的是一个对象就返回本身对象		
        */
    	if(result !== 'undefined' && /^(function|object)$/i.test(typeof result)) return result
        return obj;
    }
let sanmao = _new(Dog, '三毛');
sanmao.bark(); //=>"wangwang"
sanmao.sayName(); //=>"my name is 三毛"
console.log(sanmao instanceof Dog); //=>true

复制代码

练习题

function Fn(x, y) {
    let sum = 10;
    this.total = x + y;
    this.say = function () {
        console.log(`我计算的和是:${this.total}`);
    };
}
let res = Fn(10, 20); //普通函数执行
let f1 = new Fn(10, 20); //构造函数执行
let f2 = new Fn;
console.log(f1.sum);
console.log(f1.total);
console.log(f1.say === f2.say);
复制代码
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);		//=>false
console.log(f1.getY === f2.getY);		//=>true
console.log(f1.__proto__.getY === Fn.prototype.getY);//=>true
console.log(f1.__proto__.getX === f2.getX);		//=>false
console.log(f1.getX === Fn.prototype.getX);		//=>false
console.log(f1.constructor);			//=>Fn 函数
console.log(Fn.prototype.__proto__.constructor);	//=>Object对象
f1.getX();	//=>100
f1.__proto__.getX();	//=>undefined
//实例对象上的y
f2.getY();				//=>200
//查找原型对象上的y
Fn.prototype.getY();	//=>undefined
复制代码
function C1(name) {
    if (name) {
        this.name = name;
    }
}
function C2(name) {
    this.name = name;
}
function C3(name) {
    this.name = name || 'join';
}
C1.prototype.name = 'Tom';
C2.prototype.name = 'Tom';
C3.prototype.name = 'Tom';
alert((new C1().name) + (new C2().name) + (new C3().name));
//=>"Tom" + undefined + "join"	"Tomundefinedjoin"
复制代码

基于内置类原型的拓展方法

/*
	内置类原型指向图:arr -> Array.prototype -> Object.prototype
*/

(function(){
    const plus = function plus(num){
        return this + num
    };

    const minus = function minus(num){
        return this - num
    };

    ["plus","minus"].forEach((v) => {
        Number.prototype[v] = eval(v)
    });
})()

let n = 10;
let m = n.plus(10).minus(5);
console.log(m);//=>15(10+10-5)
复制代码

基于ES6中的CLASS重构下面的代码

/*--ES5--*/
function Modal(x,y){
    this.x=x;
    this.y=y;
}
Modal.prototype.z=10;
Modal.prototype.getX=function(){
    console.log(this.x);
}
Modal.prototype.getY=function(){
    console.log(this.y);
}
Modal.n=200;
Modal.setNumber=function(n){
    this.n=n;
};
let m = new Model(10,20);

/*--ES6 class--*/
class Model{
    /*
    	1.函数执行就会调用构造器
    	2.this.xxx = xxx 给实例设置私有属性
    */
    constructor(x,y){
        this.x = x;
        this.y = y; 
    }

    //这样处理也是给实例设置私有属性
    z = 10;
	say = function say(){};    

    /*	向类的原型对象上扩充方法
		@1 语法上必须这样写,无法扩充公有属性
		@2 所有扩充方法都是没有prototype的
	*/
    getX(){
        console.log(this.y)
    }
    getY(){
        console.log(this.y)
    }

	/*
		把其当做对象,设置静态的私有属性和方法
	*/
    static n = 200;
    static setNumber = function(n){
        this.n = n
    }
}

//ES6给类对象设置公有属性
Model.prototype.z = 10;
let m = new Model(10,20);
复制代码

进阶:函数的三种角色

/*
	Function & Object 之间的爱恨情仇
*/
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();		//=>2
getName();			//=>4
Foo().getName();	//=>1
getName();			//=>1
new Foo.getName();	//=>2
new Foo().getName();//=>3
new new Foo().getName();//=>3
复制代码

1.下面代码输出结果是什么?为啥?

/*
	1.首先push会在数组最后添加上一个元素
		Array.prototype.push = function(num){
			this[this.length] = num;
			this.length++;
			return this.length
		};
*/
let obj = {
    2: 3,
    3: 4,
    length: 2,
    push: Array.prototype.push
}
//push 方法中的this是obj 所以this.length是2 this[this.length] = 1改了2: 3
obj.push(1);	
obj.push(2);
console.log(obj);
/*
	{
  	  2: 1,
  	  3: 2,
  	  length: 4,
  	  push: Array.prototype.push	
	}	
*/
复制代码
let utils = (function(){
    function toArray(...args){
        /*
        	@1 
        		return args
        	@2
        		return [...arguments]
        	@3
        		return Array.from(arguments)
        	@3
        		return Array.prototype.slice.call(arguments)
        	@4
        		return [].slice.call(arguments)
        /* 
        
    }
    return {
        toArray
    };
})();
let ary = utils.toArray(10,20,30); //=>[10,20,30]
ary = utils.toArray('A',10,20,30); //=>['A',10,20,30]
复制代码
/*
	画图,易错
*/
function Fn() {
    let a = 1;
    this.a = a;
}
Fn.prototype.say = function () {
    this.a = 2;
}
Fn.prototype = new Fn;
let f1 = new Fn;
​
Fn.prototype.b = function () {
    this.a = 3;
};
console.log(f1.a);
console.log(f1.prototype);
console.log(f1.b);
console.log(f1.hasOwnProperty('b'));
console.log('b' in f1);
console.log(f1.constructor == Fn);
复制代码

编写queryURLParams方法实现如下的效果(至少两种方案)

/*	
	方法一 : 正则表达式
	思路:创建一个对象 用正则都把符合他捕获进去
		@1 一个哈希_HASH /#([^+=&?]+)/g
		@2 几个URL中的键值对 /([^=?#&]+)=([^=?#&]+)/g
		
		根据param返回值
*/
String.prototype.queryURLParams = function queryURLParams(param) {
    let obj = {};
    this.replace(/#([^+=&?]+)/g, (_, $1) => {
        obj["_HASH"] = $1
    })
    this.replace(/([^=?#&]+)=([^=?#&]+)/g, (_, $1,$2) => {
        obj[$1] = $2;
    })

    if(param !=="undefined") return obj[param]
    return obj
}

/*---传统字符串拼接---*/
String.prototype.queryURLParams = function queryURLParams(param){
    let str = this.split("/?")[1],
        obj = {};
    obj["_HASH"] = str.split("#")[1];
    str.split("#")[0].split("&").forEach((v,i) => {
        let key = v.split("=")[0],
            value = v.split("=")[1];
            obj[key] = value;
    })
    if(param !=="undefined") return obj[param]
    return obj
}

/*测试结果*/
let url="http://www.zhufengpeixun.cn/?lx=1&from=wx#video";
console.log(url.queryURLParams("from")); //=>"wx"
console.log(url.queryURLParams("_HASH")); //=>"video"
复制代码
重写call /apply/bind
/*
	分析call 
		@1 原理就是把fn函数放到obj中调用更改this指向
	分析bind
		@2 柯里化 , 闭包 + call
*/
(function(){
    const call = function call(obj,...args){
        //错误判断 如果是基本类型值 我们要手动Object(obj)
        obj = obj || window;
        if(!/(function|object)/i.test(typeof obj)) obj = Object(obj);
        let _id = Symbol(),
            result;;
        obj[_id] = this;
        result = obj[_id](...args);
        //删除不然会多一个
        delete obj[_id];
        return result;
    };
   const apply = function apply(obj,args){
        obj = obj || window;
        //错误判断 如果是基本类型值 我们要手动Object(obj)
        if(!/(function|object)/i.test(typeof obj)) obj = Object(obj);
        let _id = Symbol(),
            result;;
        obj[_id] = this;
        result = obj[_id](...args);
        //删除不然会多一个
        delete obj[_id];
        return result;
    };
   const bind = function bind(obj,...args){
        obj = obj || window;
        let self = this;
        return function (...event){
            self.call(obj,...[...args,...event]);
        }
    };

    ["call","apply","bind"].forEach((v,i) => {
        Function.prototype[v] = eval(v);
    });
})()

let obj = {
    name:'yzl'
}

const fn = function(x,y){
    // console.log(this,x+y);
    this.x = x;
    this.y = y;
}

/*检测call*/
fn.call(obj,10,20);
/*检测apply*/
fn.apply(obj,[10,20]);
/*检测bind*/
document.body.onclick = fn.bind(obj,10,20);
复制代码

多个call

/*
	fn1.call(fn2);
	@1 一个call 执行fn1,this指向fn2
	fn1.call.call(fn2,10,20,30);
	@2 两个及两个以上call 执行fn2,this执行10,形参20/30
*/
function fn1(){console.log(1);}
function fn2(){console.log(2);}
fn1.call(fn2);	//=>1
fn1.call.call(fn2);	//=>2
Function.prototype.call(fn1); //=>空函数返回undefined
Function.prototype.call.call(fn1);	//=>1
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享