繼 承
原型繼承
- 讓類B的原型指向類A的實例,那麼以後類B的實例既可以調取類A實例的私有屬性,也可以調取類A實例的公有屬性,那這種繼承方式就是原型繼承
- 原型繼承:繼承私有和公有
- 通過改變prototype的指向,使其指向其他實例
- 原型繼承:繼承私有和公有
function A(){
this.getX = function(){console.log('恭喜發財')}
};
A.prototype.getY = function(){
console.log('真好')
};
function B(){}
B.prototype = new A;
let f = new B;
中間類繼承
- arguments類數組,不是一個數組;雖然不是Array的實例,但是我們可以手動把arguments的__proto__指向Array的原型,那這樣arguments就可以使用Array原型上的方法了,這就是中間類繼承
- 只能繼承公有屬性
- 通過自己的__proto__指向類的prototype(原型)
- 只能繼承公有屬性
function fn(){
console.log(arguments instanceof Array)
arguments.__proto__ = Array.prototype;
console.log(arguments.push(23))
console.log(arguments)
}
fn(1,2,3,4,5)
call繼承
- call繼承:私有屬性
- 改變類裏的this指向
- 在類B中,調用了類A,並且通過call改變了類A中的this指向,使其指向B的實例;這樣類B創建的實例就具有類A的私有屬性;這種繼承就是call繼承;
- call方法在Function的原型上
/*
原型繼承:繼承私有和公有
中間類繼承:公有
call繼承:私有屬性
*/
function A(){
this.x =10
}
A.prototype.getX = function(){
console.log('萬事如意')
}
function B(){
// this->當前實例
/*
類B當做構造函數執行時,此時的this是當前實例,
*/
this.a = 20
A.call(this) // 把函數A當做普通函數執行,並且把A的this指向了類B的實例
}
//call繼承讓類B繼承了類A的私有屬性,但是不能使用類A的公有屬性
// 類B的所有實例都可以使用類A的私有屬性
let f = new B;
console.log(f)
f.getX()
寄生組合繼承
- 繼承公有和私有
- 創建一個空對象,讓空對象的__proto__指向(傳第一個參)類A的原型,在把這個空對象賦值給類B的原型
- 使用call繼承繼承了私有屬性,Object.create繼承了公有屬性,這種繼承方式就是寄生組合繼承;
- 爲了防止修改B的原型時,修改了A的原型,所以使用Object.create的方法
// Object.create(context): // 創建一個空對象,讓對象的__proto__指向你傳的第一個參數
let obj = {name:3,getX:function(){console.log(11)}}
let o = Object.create(obj)
console.log(o)
- 實例
// Object.create(context): // 創建一個空對象,讓對象的__proto__指向你傳的第一個參數
// let obj = {
// name: 3,
// getX: function () {
// console.log(11)
// }
// }
// let o = Object.create(obj)
// console.log(o);
// console.log(o.__proto__ === obj) // true
// 創建一個空對象,讓空對象的__proto__指向你傳遞的第一個參數(obj)
function A(){
this.a = 10
}
A.prototype.getX = function(){
console.log('恭喜發財')
}
function B(){
/*
函數B以構造函數的身份運行
那類B中的this指向當前實例
*/
this.x =20;
A.call(this) // 讓函數A以普通函數身份運行,而且把函數A中的this指向了類B的實例
// 繼承私有屬性
}
B.prototype = Object.create(A.prototype);// 繼承公有屬性;
// 創建一個空對象,讓空對象的__proto__指向類A的原型,在把這個空對象賦值給類B的原型
let f = new B;
f.__proto__.getY = function(){
console.log(333)
};
let m = new A;
// m.getY()
console.log(f)
//call繼承讓類B繼承了類A的私有屬性,然是不能繼承類A的公有屬性
// console.log(f)
// f.getX() // 報錯
// f.a // 可以取到
Class繼承
// ES6中class創造出來的類不能當做普通函數執行
class A {
constructor(q) {
this.x = q;
}
getX() {
console.log(this.x)
}
}
// ES6中的繼承
class B extends A {
constructor(name) {
// 子類繼承父類,可以不寫constructor,但是你要是一旦寫了,
那在constructor裏第一句話就要寫super()
// 你要是不寫constructor,那瀏覽器會默認創建一個constructor(...arg){
// super(...arg)
// }
super(200) // A.call(this, 200) 把父類當做普通函數執行,給方法傳遞參數,
讓方法中的this是子類的實例
this.y = 100;
}
getX() {
console.log(this.y)
}
}
B.prototype = Object.create(A.prototype); // class定義的類不能改原型重定向
let f = new B(100);
console.log(f)
- B.prototype = Object.create(A.prototype);
This
this詳解
- 跟函數執行有關係
- 他是js中的關鍵字,有特殊的特殊意義
- 他就是函數的執行體,誰執行函數this就是誰
- 不能給this直接賦值
- this傳的是指針,空間地址
- this是個關鍵字;在特殊的情景下,this有特殊的意義;this不能用等號對其直接修改
this的幾種情況
1.在全局作用於下,this就是window
2.在函數執行時,看執行函數前有沒有".",如果有點,那點前面是誰,this就是誰,如果沒有點,那this就是window
3.自執行函數裏的this是window
4.給元素事件行爲綁定方法,方法裏的this指向被綁定的元素本身
5.回調函數裏的this一般指向window
6.實例的私有屬性或者公有屬性裏的this一般指向當前實例
7.構造函數裏的this是當前實例
8.箭頭函數沒有this,要是在箭頭函數裏使用this,就看他上一級作用域的this,不能用call更改,不能被new
9.call、apply、bind可以改變this的指向
//2給元素的事件綁定的函數中的this,指向了當前被點擊的那個元素
box.onclick = function () {
// this : 對象
console.dir(this === box); //空間地址相同
box.style.color="red";
this.style.color = "red";
this=100;//this 不能放在等號左邊
}
box1.onclick = function () {
//this : 對象
console.dir(this);
}
//5 回調函數中的this 一般指向window
setTimeout(function () {
console.log(this);
}, 1000)
var ary = [1, 2, 3, 4];
ary.map(function () {
console.log(this);
})
//回調函數的特殊
//回調函數但是this更改了
function A(){
console.log(this);
}
function B(a){
// a();
var obj ={a:a};
obj.a();
}
B(A);
/* // var let = 3;
// console.log(let)
// console.log(this)
// console.log(this === window)
// window.a = 12;
// console.log(this.a) */
/* // function fn(){
// console.log(this)
// }
// fn()
// var age = 15;
// var obj = {
// age: 13,
// name: function(){
// console.log(this.age)
// }
// }
// obj.name() // this是obj
// var f = obj.name;
// f() // this是window*/
// (function(){
// console.log(this) // window
// })()
// box.onclick = function(){
// console.log(this)
// }
// var ary = [1, 2];
// ary.map((a,b)=>{
// console.log(this) // window
// })
// ary.sort((a,b)=>{
// console.log(this); // window
// return a-b
// })
// function fn(a){
// a()
// }
// fn(function(){
// console.log(this)
// })
// setTimeout(()=>{
// console.log(this) // window
// }, 2000)
/* var num = 100;
var obj = {
num: 2,
fn: function () {
var num = 1;
console.log(this) //window
(function (num) { // 100
// this-> window
console.log(this.num + num);//200
})(this.num)
// this->window
}
}
// obj.fn();
var f = obj.fn;
f() */
var num = 1; // 1 2
var obj = {
num: 0,
fn: function () {
num = 1;
// this=>obj
(function (num) { // 0 1
// this=>window
++this.num;
num++;
console.log(num)
})(this.num)
}
}
obj.fn();
console.log(window.num, obj.num);
改變this指向的方法
每一個函數都是Function的實例,所以每一個函數都可以調取Function原型上的方法,call,apply,bind,他們三個都可以改變函數裏的this指向
call
- call繼承:私有屬性
- fn通過__proto__屬性找到當前所屬類的原型(Function的原型)上的call方法
- 所有函數可以獲取到call
- 讓call方法執行,並且給call傳遞實參
- 在call方法執行的同時,也讓fn執行,並且把fn的this指向了第一個參數
- 注意事項
- 在嚴格模式下,如果call不傳參或者傳undefined,那fn的this就是undefined,如果傳null,那fn的this就是null
- 在非嚴格模式下,如果call不傳參或者傳undefined或者傳null,那fn的this都是window
- call的第一個參數是fn的this指向,從第二個開始,就是fn的正常參數了
- fn通過__proto__先找到Function原型中的call方法,讓call方法執行,call運行時,改變了call的this的this指向,fn中的this指向call的第一個參數,並且讓call中this執行;
function fn1(){
console.log(100);
console.log(this);
}
function fn2(){
console.log(200);
}
//fn.call(1)
fn1.call.call.call.call(fn2);
// 1. fn1.call.call.call -->this--> fn2;
// 2. fn1.call.call.call()
// 1.fn2--> this 沒有變
// 2.fn2();
// 1.先執行後面的call方法(這個call方法中的this是fn1.call);
是改變fn1.call中的this指向fn2;並且讓fn1.call運行;
// 2. 當fn1.call運行時,改變fn.call中的this的this指向沒有發生改變,
繼續讓fn1.call中this執行,也就是讓fn2運行;
-------------------------------------------------------
function B(){
}
console.log(B.name);// 對象
function A(){
}
A.call()
function B(){
}
A.call.call.call(B);// B執行,並且B中的this一定指向window
最後一個call執行時, 把A.call.call中的this改成了函數B;並且讓A.call.call執行;
當A.call.call執行時,把函數B的this改成window,並且讓B執行;
// "use strict"
function fn(n,m){
console.log(this)
}
let obj = {
name:3
}
// fn(12,13)
// 實現把函數裏的this改爲obj
// obj.fn = fn;
// obj.fn();
// delete obj.fn;
fn.call(undefined)
call方法封裝
function myCall(context, ...arg) { // 收縮運算符
// arg接收的是傳遞的從第二個開始的實參
// this->fn context->obj
let res = null; // 初始化一個實例的返回值
context = context || window
// 處理傳參的特殊情況,如果傳的是空、null和undefined,context的值就是window
context.$fn = this // 把當前實例放到對象裏
res = context.$fn(...arg); // 讓this執行(讓當前實例執行)
delete context.$fn // 在對象裏刪除那個實例
return res;// 把this執行之後的返回值return 出去
}
Function.prototype.myCall = myCall;
function fn() {
console.log(this)
return 1
}
let obj = {
name: 3
}
// console.log(fn.call(1))
console.log(fn.myCall(obj, 12, 23))
console.log(obj)
連call面試題
- 如果有兩個及以上call,那最後就是執行傳入的參數
<script>
function fn1() {console.log(1)}
function fn2() {console.log(2)}
// fn1.call(fn2); //1
// fn1.call.call(fn2); //2 不管前邊有多少call,他執行的是最後一個call方法
// Function.prototype.call(fn1); // 不輸出
// Function.prototype.call.call(fn1); // 1
Function.prototype.call.call.call.call(fn1);
function myCall(context){
// 如果有兩個及以上call,那最後就是執行傳入的參數,傳入的參數的this指向window
/*
fn1.call(fn2);
context->fn2 this->fn1
context.$fn = this ->fn2.$fn = fn1
context.$fn() ->fn1()
*/
/*
fn1.call.call(fn2)
context ->fn2 this-> fn1.call ->call
context.$fn = this ->fn2.$fn = call
context.$fn() ->fn2.$fn() ->call()
第二次執行
context->window this->fn2
context.$fn = this ->window.$fn = fn2;
context.$fn() ->window.$fn() ->fn2()
*/
/*
Function.prototype.call(fn1)
context ->fn1 this->Function.prototype【原型】
context.$fn = this ->fn1.$fn = 【原型】
context.$fn() ->fn1.$fn() ->【原型】()
*/
/*
Function.prototype.call.call(fn1)
context ->fn1 this -> Function.prototype.call ->call
context.$fn = this ->fn1.$fn = call
context.$fn() ->fn1.$fn() ->call()
第二次執行
context->windwo this->fn1
context.$fn = this ->window.$fn = fn1;
context.$fn() ->window.$fn ->fn1()
*/
context.$fn = this;
context.$fn()
}
</script>
apply
- 改變this指向:他和call方法一樣,只不過傳參不同,第二個參數必須是數組或者類數組
- 傳入數組,但是Fn實際接收的仍然是一個一個接收;
function fn(a,b){
console.log(a,b);
console.log(this);
}
fn.apply(null,[100,200])
----------------------------------------
function fn(n,m){
console.log(this, n, m)
}
fn.apply(1, [20,30])
bind
- 預處理this
- 這個方法也是改變this指向的,但他會提前改變實例函數的this指向,並不會讓實例函數執行,他的返回值是改變this之後的新函數
- 在bind函數中將fn進行了包裝和處理,改變了fn裏面的this指向,並且返回一個改變this之後的新函數
- bind在IE8以下不兼容,bind正常傳參
function fn(a,b){
console.log(a,b);
console.log(this);
}
var f = fn.bind([1,2]);
f();
var f =fn.bind([1,2]);
f(100,200);
fn();
--------------------------------------------------------
<div id="box">1111</div>
// let box = document.getElementById('box');
let fn = function(){
console.log(this)
}
let obj = {}
// box.onclick = fn.bind(obj)
fn = fn.bind(obj)//不改變原有函數,需要重新賦值
fn()