面向对象基础
构造函数和普通函数执行区别
/*
普通函数执行:
+ 初始化作用域链
+ 初始化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
复制代码
梳理一下原型链
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()等方法
*/
复制代码
检测某个属性是否为对象的“私有属性”
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