JAVA面向物件02---類與物件關係,建立與物件,小結
阿新 • • 發佈:2022-04-09
原型鏈
每建立一個函式,解析器都會想函式中新增一個prototype原型屬性,這個prototype屬性對應著一個物件,這個物件就是原型物件
如果該函式是普通函式呼叫,則prototype沒作用
如果該函式是以建構函式呼叫,則建立的物件中都會有一個隱含的屬性,指向該建構函式的原型物件,通過prototype來訪問該屬性
物件.hasOwnProperty(屬性) //物件自身中是否包含某種屬性
原型物件也是物件,所以也有原型 object物件沒有原型
物件要使用某個屬性或方法,會現在自身尋找→原型物件中尋找→原型物件的原型中尋找......直到object物件原型
陣列
會影響原陣列的陣列方法:push,pop,shift,unshift,reverse,splice,sort
<script>
let arr = [1,2,3,4,5]
//返回擷取從0到3組成的新陣列
console.log(arr.slice(0,3));
//返回刪除索引0開始的1個元素 並用22,33插入刪除的位置
console.log(arr.splice(0,1,'22','33'));
</script>
陣列去重
<script>
let arr = [1, 2, 2, 2, 3, 4, 5, 2, 3, 1, 2, 0]
for (let i = 0; i < arr.length; i++) {
for (let j = i + 1; j < arr.length; j++) {
if (arr[i] == arr[j]) {
arr.splice(j, 1)
j--
}
}
}
console.log(arr.sort())
</script>
######
this的情況
一、隱式繫結 沒有 物件.函式 指向window
--函式呼叫時前面並未指定任何物件,這種情況下this指向全域性物件window。
function fn1() {
let fn2 = function () {
console.log(this); //window
fn3();
};
console.log(this); //window
fn2();
};
function fn3() {
console.log(this); //window
};
fn1();
--如果函式呼叫時,前面存在呼叫它的物件,那麼this就會隱式繫結到這個物件上
函式呼叫前存在多個物件,this指向距離呼叫自己最近的物件
function fn() {
console.log(this.name);
};
let obj = {
name: '行星飛行',
func: fn,
};
let obj1 = {
name: '聽風是風',
o: obj
};
obj1.o.func() //行星飛行
二、 隱式丟失
在特定情況下會存在隱式繫結丟失的問題,最常見的就是作為引數傳遞以及變數賦值
var name = '行星飛行';
let obj = {
name: '聽風是風',
fn: function () {
console.log(this.name);
}
};
function fn1(param) {
param();
};
fn1(obj.fn);//行星飛行
// obj.fn 也就是一個函式傳遞進 fn1 中執行,單純傳遞了一個函式而已,this並沒有跟函式綁在一起,所以this丟失這裡指向了window。
var name = '行星飛行';
let obj = {
name: '聽風是風',
fn: function () {
console.log(this.name);
}
};
let fn1 = obj.fn;
fn1(); //行星飛行
三、顯式繫結(通過call、apply以及bind方法改變this的行為)
call與apply讓函式從被動變主動,函式能主動選擇自己的上下文,以此方法改變this指向時,指向引數提供的是null或者undefined,那麼 this 將指向全域性物件。
let obj1 = {
name: '聽風是風'
};
let obj2 = {
name: '時間跳躍'
};
var name = '行星飛行';
function fn() {
console.log(this.name);
};
fn.call(obj1); //聽風是風
fn.apply(obj2); //時間跳躍
fn.call(undefined); //行星飛行
fn.apply(null); //行星飛行
fn.bind(undefined)(); //行星飛行
call、apply與bind有什麼區別?
1.call、apply與bind都用於改變this繫結,但call、apply在改變this指向的同時還會執行函式,而bind在改變this後是返回一個全新的boundFcuntion繫結函式,這也是為什麼上方例子中bind後還加了一對括號 ()的原因。
2.bind屬於硬繫結,返回的 boundFunction 的 this 指向無法再次通過bind、apply或 call 修改;call與apply的繫結只適用當前呼叫,呼叫完就沒了,下次要用還得再次綁。
let obj1 = {
name: '聽風是風'
};
let obj2 = {
name: '時間跳躍'
};
var name = '行星飛行';
function fn() {
console.log(this.name);
};
fn.call(obj1); //聽風是風
fn(); //行星飛行
fn.apply(obj2); //時間跳躍
fn(); //行星飛行
let boundFn = fn.bind(obj1);//聽風是風
boundFn.call(obj2);//聽風是風
boundFn.apply(obj2);//聽風是風
boundFn.bind(obj2)();//聽風是風
3.call與apply功能完全相同,唯一不同的是call方法傳遞函式呼叫形參是以雜湊形式,而apply方法的形參是一個數組。在傳參的情況下,call的效能要高於apply,因為apply在執行時還要多一步解析陣列。
let obj = {
name: '聽風是風'
};
function fn(age,describe) {
console.log(`我是${this.name},我的年齡是${age},我非常${describe}!`);
};
fn.call(obj,'26','帥');//我是聽風是風,我的年齡是26,我非常帥
fn.apply(obj,['26','帥']);//我是聽風是風,我的年齡是26,我非常帥
四、new繫結
function Fn(){
this.name = '聽風是風';
};
let echo = new Fn();
echo.name//聽風是風
五、this的繫結優先順序
顯式繫結 > 隱式繫結 > 預設繫結
new繫結 > 隱式繫結 > 預設繫結
六、箭頭函式的this
箭頭函式中沒有this,箭頭函式的this指向取決於外層作用域中的this,外層作用域或函式的this指向誰,箭頭函式中的this便指向誰。一旦箭頭函式的this繫結成功,也無法被再次修改,可以通過修改箭頭函式的外層作用域達到修改箭頭函式的this。
function fn() {
return () => {
console.log(this.name);
};
};
let obj1 = {
name: '聽風是風'
};
let obj2 = {
name: '時間跳躍'
};
fn()
fn.call(obj1)(); // fn this指向obj1,箭頭函式this也指向obj1
fn.call(obj2)(); //fn this 指向obj2,箭頭函式this也指向obj2
Math
Math.ceil(浮點數) 小數向上取整
Math.floor(浮點數) 小數向下取整
Math.round(浮點數) 小數四捨五入
Math.max(數字,數字,數字) 取數字們中最小的
Math.max(數字,數字,數字) 取數字們中最大的
生成一個x,y之間的隨機數
Math.round(Math.random()*(y-x)+x)
樣式
獲取當前元素樣式值:元素.currentStyle.樣式名 IE瀏覽器高光時刻
getComputedStyle(元素,null).樣式名 其他瀏覽器
<script>
// obj:元素 name:樣式名 獲取元素對應的樣式值
function getStyle(obj,name){
if(window.getComputedStyle){
return getComputedStyle(obj,null)[name]
}else{
return obj.currentStyle[name] //IE專屬
}
}
</script>
事件物件
當事件的響應函式被觸發時,瀏覽器每次都會將一個事件物件作為實參event傳遞進響應函式
盒子隨著滑鼠移動而移動 clientX/Y pageX/Y 相對於可視視窗/整個頁面的滑鼠位置
事件引數的相容
event = event||window.event
事件冒泡
所謂冒泡指的就是事件的向上傳導,當後代元素的事件被觸發時,其祖先元素的相同事件也會被觸發
取消事件冒泡
event.cancelBubble = true
event.target 當前觸發事件的物件
事件委派
要實現只繫結依次事件,即可應用到多個元素上,即使元素是後新增的
--將事件繫結到元素的共同的祖先元素,後代元素事件通過冒泡傳遞給祖先元素
--祖先元素使用event.target.className=='name'接受期望元素繫結,其餘不繫結
--減少繫結次數,提高程式效能
拖拽事件
鍵盤事件
String.fromCharCode(數字) 鍵盤AC碼轉為字元
<script type="text/javascript">
document.onkeydown=function(e){
//String.fromCharCode(數字) 數字轉字元
console.log(String.fromCharCode(e.keyCode));
}
</script>
方塊隨著方向鍵移動練習
BOM
window:整個視窗
navigator:視窗資訊
location:導航條資訊
history:前進後退鍵資訊
screen:螢幕資訊
Json
本質就是特殊格式的字串使得所有語言都能識別和轉化,主要用於資料互動
Json物件 '{"屬性名1":"屬性值1","屬性名2":"屬性值2"......}' 屬性名值都要用雙引號包裹
Json陣列 '[1,2,3,4.....]'
方法: Json.parse(Json物件) 將Json物件轉化為Js物件
Json.stringfy(Js物件) 將Js物件轉化為Json物件
eval函式 能夠用來執行一段字串形成的js程式碼,並返回結果
如果eval中會將{}識別為程式碼塊 用()包裹就不會被識別為程式碼塊
<script type="text/javascript">
let str = "alert('hello')"
eval(“("+str+")”) //彈出hello
</script>
JavaScript高階
資料型別
typeof 返回資料型別的字串表示 不能判斷null和object object和array
<script type="text/javascript">
let str = 'xy'
console.log(typeof str === 'string') //true
console.log(typeof str === String) //false
</script>
A instanceof B A是否為B的例項 如果B函式的顯示原型物件在A物件的原型鏈上則為true 反之false
VC <script type="text/javascript">
let obj = { name: 'wkq', age: 23 }
console.log(obj instanceof Object); //true
</script>
undefined和null的區別
undefined是定義但未賦值,null是定義賦值但值為null
何時使用null
初始時,物件賦值為null,用null表示將要賦值為物件
結束時 物件賦值為null 讓物件成為垃圾物件被處理
資料(堆資料)|記憶體|變數(棧索引)
1、資料是儲存在記憶體代表特定資訊的東東
例如:var a = '基本資料/物件|方法/變數' a儲存的是資料/物件|方法地址/變數記憶體內容
2、記憶體是記憶體條通電後產生的可儲存資料的空間 斷電後空間和資料都消失
3、變數=變數名+變數值 對應一塊小記憶體 變數名用來查詢對應的記憶體 變數值就是記憶體中儲存的資料
回撥函式
定義了 沒有呼叫 最終執行了
IIFE 函式立即執行表示式|匿名函式自呼叫
作用:隱藏實現,不會汙染外部名稱空間 可以暴露方法
<script type="text/javascript">
// 函式自呼叫
(function(){
var a =3
console.log(a); //3
})()
var a = 4
console.log(a); //4
// ;使得不會認為是整體
;(function(){
var a =1
function test(){
console.log(++a);
}
window.$ = function(){
return {
test:test //返回物件 對外暴露test方法
}
}
})()
$().test()
</script>
原型
*每個函式定義時自動新增prototype屬性,預設指向原型物件(空的object)
*原型物件中有個屬性constructor,指向函式物件
<script type="text/javascript">
console.log(Date.prototype.constructor === Date) //true
</scripWt>
*可以給原型物件新增屬性(方法)
*每個物件建立時自動新增 __proto__ 屬性,預設值為建構函式的prototype屬性值
<script type="text/javascript">
function Fn(){
}
let fn = new Fn()
console.log(Fn.prototype===fn.__proto__); //true
</script>
原型鏈
物件要使用某個屬性或方法,會現在自身尋找→原型物件中尋找→原型物件的原型中尋找......直到object物件原型
原型繼承
建構函式的例項物件自動擁有建構函式原型物件的屬性和方法,利用的就是原型鏈
例項的隱式原型等於構造的顯示原型 二者指向同一個原型物件(空的object)
如果顯示原型重新指向,會斷開之前指向的原型物件,指向新的原型物件。而隱式原型物件依然指向之前的原型物件,不會隨著顯示原型的重新指向而改變指向
<script type="text/javascript">
function A() {}
A.prototype.n = 1
var b = new A()
A.prototype ={
n:2,
m:3
}
var c = new A()
console.log(b.n,b.m,c.n,c.m); //1 undefined 2 3
</script>
原型鏈上沒有 例項的隱式原型→Function的顯示原型的路徑 如果例項訪問Function上的方法 會報錯
<script type="text/javascript">
function F(){
Object.prototype.a = function(){
console.log('a()');
}
Function.prototype.b = function(){
console.log('b()');
}
}
var f = new F()
f.a()
// f.b()
F.a()
F.b()
console.log(F);
console.log(Object.prototype);
console.log(Function.prototype); // ƒ () { [native code] }
console.log(Function instanceof Object); //true 函式是物件的例項
</script>
變數提升與函式提升 先執行變數提升再執行函式提升
變數提升
<script type="text/javascript">
var a =3
function fn(){
console.log(a);
var a =4
}
fn() //undefined
</script>
相當於
<script type="text/javascript">
var a =3
function fn() {
var a
console.log(a)
a = 4
}
fn() //undefined
</script>
函式提升
<script type="text/javascript">
fn() //fn() 函式提升
function fn(){
console.log('fn()');
}
</script>
執行上下文
當前程式碼執行環境,執行程式碼需要哪些資料提前準備好再開始執行
<script type="text/javascript">
/* 測試一 */
console.log('global begin:' + i) //undefined
var i = 1
foo(1)
function foo(i) {
if (i == 4) {
return
}
console.log('foo() begin:' + i)
foo(i + 1)
console.log('foo() end:' + i)
}
console.log('global end:' + i)
/* 測試二 */
var c = 1
function c(c) {
console.log(c)
}
c(2) //報錯
//相當於
var c
function c(c) {
console.log(c)
}
c=1
c(2) //此時c=1 不是一個方法所以報錯
/* 測試三 */
if (!(b in window)) {
var b = 1
}
console.log(b) //undefined
</script>
作用域和作用域鏈
作用域只能向外查詢,不能向內查詢。當需要某個屬性時,向外找最近的祖先作用域裡的同名屬性
<script type="text/javascript">
var a =10,b=20
function fn(x){
var a = 100,c=300
console.log('fn()',a,b,c,x);
function bar(x){
var a =1000,d=400
console.log('bar()',a,b,c,d,x);
}
bar(100) //1000,20,300,400,100
bar(200) //1000,20,300,400,200
}
fn(10) //100,20,300,10
</script>
函式呼叫函式的情況下,考慮被呼叫函式的作用域向外
<script type="text/javascript">
var x =10
function fn(){
console.log(x);
}
function show(f){
var x =20
f()
}
show(fn) //10
</script>
物件屬性名 不包括在函式作用域內
<script type="text/javascript">
var fn = function () {
console.log(fn)
}
fn() //fn()
var obj = {
fn2: function () {
console.log(fn2)
},
}
obj.fn2() //錯誤 函式內部作用域並沒有fn2
</script>
閉包
定義:就是包含被引用變數的物件
巢狀情況下 內部函式引用了外部函式或變數時,產生閉包
將函式作為另一個函式的返回值 產生閉包
將函式作為實參傳遞給另一個函式呼叫 產生閉包
<body>
<button>按鈕一</button>
<button>按鈕二</button>
<button>按鈕三</button>
<script type="text/javascript">
var btns = document.getElementsByTagName('button')
for (let i = 0,length =btns.length; i < length; i++) {
btns[i].addEventListener('click', function () {
alert('第'+(i+1)+'按鈕被按了') //捕獲i i在function之外
})
}
</script>
</body>
作用
使用函式內部的變數在函式執行完後,仍然存活在記憶體中(延長區域性變數的生命週期
讓函式外部可以操作到函式內部(區域性變數)的資料
生命週期
產生:巢狀內部函式定義完成後產生
死亡:包含閉包的函式物件成為垃圾物件時
自定義JS模組
index.js
(function mymodule(){
var xy ='wkqXY'
function toUp(){
console.log('toUP'+xy.toUpperCase());
}
function toLow(){
console.log('toLow'+xy.toLowerCase());
}
window.mymodule={ //放在window上 匯入該js檔案即可使用
toUp,
toLow
}
})()
index.html
<script src="./index.js"></script>
<body>
<script type="text/javascript">
mymodule.toUp()
mymodule.toLow()
</script>
</body>
缺點
記憶體洩露
函式執行完後,函式內的區域性變數沒有釋放,佔用記憶體時間會變長 解決:及時釋放
記憶體洩露多了就會導致記憶體溢位
意外的全域性變數/定時器沒有清理/閉包都會導致記憶體洩露
記憶體溢位
當程式執行需要的記憶體超過了剩餘的記憶體,就會丟擲記憶體溢位的錯誤
禿頂題
<script type="text/javascript">
function fun(n,o){
console.log(o);
return {
fun:function(m){
return fun(m,n)
}
}
}
var a = fun(0) //undefined
a.fun(1)
a.fun(2)
a.fun(3)
var b =fun(0).fun(1).fun(2).fun(3)
var c =fun(0).fun(1)
c.fun(2)
c.fun(3)
</script>
返回一個物件的函式就是工廠函式
程序和執行緒
程序:程式的一次執行,它會佔用一片獨有的記憶體空間 程序之間相互獨立
多程序:一應用程式可以同時啟用多個例項執行
執行緒:是程序內的一個獨立執行單元,CPU最小的排程單元
單執行緒:
優:順序程式設計簡單易懂 缺:效率低
多執行緒:
優:提升CPU利用率 缺:建立多執行緒開銷/執行緒間切換開銷/死鎖和狀態同步問題
關於JS
JS是單執行緒執行的,
Js引擎執行程式碼的基本流程
1、先執行初始化程式碼
*設定定時器
*繫結監聽
*傳送網路請求
2、後面某個時刻執行回撥程式碼(回撥函式中的程式碼)
Array.filter(()=>{})
//用filter中的回撥過濾陣列中的值,返回新陣列
String.charAt(i)
//返回字串的第i-1個字元
String.padStart(x,y)
x為字串長度,y為補全字元
String.padEnd(x,y)
返回新的字串,表示用引數字串從右側補全原字串。
未完待續....