JS进阶
补充1:call、apply、bind的用法
原本this指向:谁调用的方法,this就指向谁
作用:调用函数、改变this指向、借用别的对象的方法
第一个参数为null或' ',this指向window对象
var name = "window中的name"
function getName(){
name = "function中的name"
cconsole.log(this.name);
}
getName();//此处为function中的name,若function中没有name则到window中找
getName.apply();
区别:call和bind传参是单个参数,apply传参是数组参数;call和apply直接调用函数,直接生效,bind返回一个新的方法,需要另行调用。
补充2:this指向问题
注意:this在strict模式下指向不是window对象
1、通常情况
一般来说:一般函数中的this指向window对象
function a(){
var user = "小明";
console.log(this.user); //undefined
console.log(this); //Window
}
window.a();
2、对象中的方法
指向调用函数者(人为改变this指向除外)
var o = {
user:"小明",
fn:function(){
console.log(this.user); //小明
}
}
//var o = {}
//Object.defineProperty(o,"user",{
// value:"小明"
//});
o.fn();
注意:以下这种情况,this指向window对象
var j = o.fn;
j();
3、实例对象调用构造函数
指向实例对象
function Person(age, name) {
this.age = age;
this.name = name
console.log(this) // 此处 this 分别指向 Person 的实例对象 p1 p2
}
var p1 = new Person(18, 'zs')
var p2 = new Person(18, 'ww')
注意:原型对象如果返回值是一个对象,那么this指向的就是那个返回的对象,如果返回值不是一个对象那么this还是指向函数的实例。
function fn()
{
this.user = '小明';
return {};
//return function(){};
//return 1;
//return undefined;
//return null;
}
var a = new fn;
console.log(a.user); //undefined、undefined、小明、小明、小明
4、通过事件绑定的方法
this 指向绑定事件的对象
var oBtn = document.getElementById("btn");
oBtn.onclick = function() {
console.log(this); // btn
}
5、定时器函数
此时 this 指向 window
setInterval(function () {
console.log(this); // window
}, 1000);
1.创建对象
属性命名规则 私有成员:user,伪私有成员:user,普通成员:user
1-1、普通对象创建方法
var obj = {
name:"小明",
set:function(){
console.log(this.name);
}
}
1-2、基于Object构造函数
var obj = new Object();
obj.name = "小明"
obj.set = function (){
console.log(this.name)
}
1-3、基于对象字面量
var obj = {
name:"小明",
getName:function(){
console.log(this.name);
},
address:{
name:"上海",
tel:"110",
},
}
1-4、基于工厂模式批量创建对象
function createObj(name,address){
var obj = new Object();
obj.name = name;
obj.getName = function(){
console.log(this.name)
}
obj.address = address;
return obj;
}
var person = createObj("小明",{
name:"上海",
tel:"110",
})
1-5、基于构造函数创建对象
通过this为对象添加属性,用new操作符为对象创建实例
function createObj(name,address){
this.name = name;
this.getName = function(){
return this.name;
}
this.address = address;
}
var person = new createObj("小明",{
name:"上海",
tel:"110",
})
1-6、基于原型对象的模式
将所有函数和属性都封装到原型对象的prototype属性上
function Person(){
Person.prototype.name = "小明";
Person.prototype.getName = function(){
return this.name;
}
Person.prototype.address = {
name:"上海",
tel:"110",
};
}
var person = new Person();
var person2 = new Person();
1-7、构造函数和原型混合使用的模式(比较好的方法)
//构造函数方法定义对象中的属性
function Person(name,address){
this.name = name;
this.address = address;
}
//在原型中添加所有实例共享的函数
Person.prototype.getName = function(){
return this.name;
}
//生成实例
var person = new Person("小明",{
name:"上海",
tel:"110",
})
1-8、动态原型模式
function Person(name,address){
this.name = name;
this.address = address;
if(typeOf Person._initialized === "undefined"){
Person.prototype.getName = function(){
return this.name;
};
Person._initialized = true;
}
}
//生成实例
var person = new Person("小明",{
name:"上海",
tel:"110",
})
2.对象克隆
克隆:将某个变量的值复制到另一个变量上,根据原始变量和复制的变量的值之间相互影响的情况,分为深克隆和浅克隆。
对于不同的数据类型(基本数据类型和引用数据类型),深浅克隆也会有不同的表现。
基本数据类型:变量存放的是变量本身的值,可以直接访问
引用数据类型:变量存放的是值在内存中的地址,地址指向内存中的某个位置,如果多个变量指向同一位置,那么对变量进行修改会影响多个变量的值
深克隆与浅克隆 区别:浅克隆的原始变量和克隆变量互相影响,深克隆的原始变量和克隆变量相互独立不受影响
2-1、简单的引用复制(浅克隆)
var a = {
name:"小明",
age:18,
}
var b = a;
2-2、ES6的Object.assign()函数(浅克隆)
作用:将原对象的可枚举属性赋值到目标对象中
var result = Object.assign({},origin)
2-3、JSON序列化和反序列化(深拷贝)
深拷贝还有:arr.concat、arr.slice(缺点:都只能深拷贝一层的数据)
如果一个对象中所有属性都是可序列化的,可以使用JSON.stringify()函数将原始对象序列化为字符串,再使用JSON.parse()函数将字符串反序列化为一个对象。
var origin = {
a:1,
b:[2,3,4],
c:{
d:"name",
}
}
var result = JSON.parse(JSON.stringify(origin))
这种方法有几个问题:
- 无法实现对函数、RegExp等特殊对象的深拷贝
- 对象的constructor会被抛弃,所有构造函数会指向Object,原型链关系会被破坏
- 对象中存在循环引用会抛出异常
(function(_){
var types = "Array Object Number String Date RegExp Boolean Null Undefined".split(" ")
// console.log("type",type);
function type(){
}
for(var i=0;i<types.length;i++){
_['is'+types[i]] = (function(){
// console.log("_",_);
return function(param){
return type.call(param)
}
})(types[i])
}
return _;
})(_={})
### 原型对象
原型对象、构造函数、实例对象
通过new操作符创建一个实例对象,它的 proto 属性指向构造函数的原型对象,默认情况下,原型对象的constructor属性指向prototype属性所在的函数(即构造函数)
对象重写
//原来
function Person(){}
Person.prototype.name = "小明"
Person.prototype.age = 18
//重写
function Person1(){}
Person1.prototype = {
constructor:Person,//加入对象的指向
name:"小明",
age:18,
}
原型链
function Person(){}
var person = new Person();
person._proto_ = Person.prototype;
person._proto_._proto_ = Person.prototype._proto_ = Object.prototype;
person._proto_._proto_._proto_ = Object.prototype._proto_ = null;
__proto__是每个对象都有的属性
prototype是函数才有的属性
继承
继承、封装、多态是面向对象语言的三大特性,JavaScript不是一门面向对象的语言,所以不具备继承的特性
function Animal(name){
this.type = "animal"
this.name = name
function sleep(){
console(this.name+"正在睡觉");
}
}
function Cat(name){
this.name = name
function eat(food){
console.log(this.name+"正在吃"+food);
}
}
//原型链继承
Cat.prototype = new Animal();
//将Cat的构造函数指向自身
Cat.prototype.constructor = Cat;
var cat = new Cat("加菲猫");
console.log(cat.type)
//将Cat的构造函数指向自身 Cat.prototype.constructor = Cat;
不写这行代码,Cat的构造函数将指向父类,父类的方法和属性将优先。
缺点:
- 原型链继承时,改写了子类Cat的prototype属性,将其指向Animal的一个实例对象,那么所有Cat的实例对象都可以共享Animal实例的属性,如果Animal中有属性的值为引用类型,那么改变Cat实例的值将会改变其他实例的属性值。
- 创建子类继承时,无法向父类的构造函数传递参数
- 无法实现多继承
构造继承
function Animal(name){
this.type = "animal"
this.name = name
function sleep(){
console(this.name+"正在睡觉");
}
}
Animal.prototype.eat = function(food){
console.log(this.name+"正在吃"+food);
}
function Cat(name){
//继承
Animal.call(this);
this.name = name
}
//Cat实例不能使用原型链上的方法eat
优点:
- 引用数据类型不会相互影响
- 可以通过多次调用call实现多继承
缺点:
- 伪继承,只是子类实例的继承,不是父类的继承 cat instant Cat true,cat instant Animal false
- 伪继承,子类的实例不能使用父类原型对象上的属性和方法
- 每次子类实例化都要使用call重新绑定this指向
复制继承
主要思想:首先生成父类的实例,通过遍历父类的属性和函数,将其依次设为子类实例或原型对象的属性和函数
function Animal(name){
this.name = name
this.sleep = function(){
console.log(this.name+"正在睡觉");
}
}
Animal.prototype.eat = function(food){
console.log(this.name+"正在吃"+food);
}
function Cat(name){
var animal = new Animal("tom");
for(key in animal){
if(animal.hasOwnProperty(key)){
this[key] = animal[key];
}else{
Cat.prototype.key = animal[key];
}
}
return this.name;
}
缺点:
- 所有属性都要复制,浪费内存
- 实例只是子类的实例,不是父类的实例
#### 组合继承
方法:结合构造继承和原型继承两种方法,在子类的构造函数中通过call调用父类的构造函数,将父类实例的属性和函数绑定到子类的this中,同时,改变子类的prototype属性,继承父类原型对象上的属性和函数。
function Animal(name){
this.name = name
this.sleep = function(){
console.log(this.name+"正在睡觉");
}
this.feature = ['fat','thin','tall'];
}
Animal.prototype.eat = function(food){
console.log(this.name+"正在吃"+food);
}
function Cat(name){
//通过构造函数继承实例的属性和函数
Animal.call(this);
this.name = name
}
//原型链继承
Cat.prototype = new Animal();
//将构造函数绑定到自己身上
Cat.prototype.constructor = Cat;
优点:
-
既能继承父类实例的属性和函数,又能继承原型对象的属性和函数
-
既是父类的实例,又是子类的实例cat instant Cat true,cat instant Animal true
-
不会出现引用属性共享的问题
在子类的构造函数中将父类的实例属性指向子类的this,即使后面将父类的实例属性绑定到子类的prototype属性上,也会由于构造函数作用域优先级比原型链优先级高,不会出现引用属性共享的问题
-
可以向父类的构造函数传递参数
通过call函数向父类的构造函数传递参数
缺点:
-
父类的实例属性绑定了两次
通过call函数调用了一次父类的构造函数,改写子类的prototype属性,生成父类的实例时又调用了一次父类的构造函数
寄生组合继承
function Animal(name){
this.name = name
this.sleep = function(){
console.log(this.name+"正在睡觉");
}
this.feature = ['fat','thin','tall'];
}
Animal.prototype.eat = function(food){
console.log(this.name+"正在吃"+food);
}
function Cat(name){
//通过构造函数继承实例的属性和函数
Animal.call(this);
this.name = name
}
(function(){
//设置任意函数Super()
var Super = function(){}
//关键:Super函数的原型指向父类Animal的原型,去掉父类的实例属性
Super.prototype = Animal.prototype;
//原本是Cat.prototype = new Animal();
Cat.prototype = new Super();
Cat.prototype.constructor = Cat;
})();
//实例化时要先animal再cat(先父类后子类)
同步是指:当程序1调用程序2时,程序1停下不动,直到程序2完成回到程序1来,程序1才继续执行下去。 异步是指:当程序1调用程序2时,程序1径自继续自己的下一个动作,不受程序2的的影响。
Object类型及其实例和静态函数
new操作符
HTMLCollection对象和NodeList对象
getElementsByTagName() 方法返回 HTMLCollection对象
HTMLCollection 对象类似包含 HTML 元素的一个数组
length属性:HTMLCollection 对象的 length 属性定义了集合中元素的数量
ps:HTMLCollection 无法使用数组的方法: valueOf(), pop(), push(), 或 join()
NodeList 对象是一个从文档中获取的节点列表 (集合) 。
一些旧版本浏览器中的方法(如:getElementsByClassName() )返回的是 NodeList 对象,而不是 HTMLCollection 对象。
所有浏览器的 childNodes 属性返回的是 NodeList 对象。
大部分浏览器的 querySelectorAll() 返回 NodeList 对象。 相似与区别:
- HTMLCollection是 HTML 元素的集合,NodeList 是一个文档节点的集合。
- NodeList 与 HTMLCollection 都与数组对象有点类似,可以使用索引 (0, 1, 2, 3, 4, ...) 来获取元素。
- NodeList 与 HTMLCollection 都有 length 属性。
- HTMLCollection 元素可以通过 name,id 或索引来获取,NodeList 只能通过索引来获取。
- 只有 NodeList 对象有包含属性节点和文本节点。
事件流
默认的事件流是冒泡的,默认值为false,从内到外冒泡,改为true变为捕获,从外到内捕获。
btn.addEventListener('click',function(){
console.log(1);
},true)//捕获
#### 事件处理程序
DOM0级事件处理程序
:事件之间会相互覆盖,
btn.onclick = function(){
console.log(1);
}
DOM2级事件处理程序
:事件属性之间不会相互覆盖,可以区分冒泡和捕获
btn.addEventListener('click',function(){
console.log(1);
},true)//捕获
DOM3级事件处理程序
允许自定义事件,自定义事件由createEvent()函数创建,返回的对象有一个initCustomEvent()函数,通过传递对应的参数可以实现自定义事件。
type:字符串,触发的事件类型
bubble:布尔型,表示事件是否可以冒泡
cancelable:布尔型,表示事件是否可以取消
detail:对象,任意类型,保存在Event对象的detail属性中
document.implementation.hasFeature('CostomEvents','3.0');//浏览器是否支持DOM3级事件处理程序
实现步骤:
-
创建自定义事件
var customEvent; (function(){ if(document.implementation.hasFeature('CostomEvents','3.0')){ var detailData = {name:"小明"}; customEvent = document.createEvent('customEvent'); customEvent.initCustomEvent('myEvent',true,false,detailData); } })(); -
监听自定义事件
//获取元素 var div = document.querySelector('#className'); //监听myEvent事件 myEvent.addEventListener('myEvent',function(e)=>{ console.log(e.detail); }) -
触发自定义事件
var btn = document.querySelector('#className'); btn.addEventListener('click',function()=>{ div.dispatchEvent(customEvent); })
Event对象
概念: 事件发生后,跟事件相关的一系列信息数据的集合都放在这个对象里,这个对象就是事件对象。如鼠标事件,就会得到鼠标的相关信息。
事件对象的使用:事件触发就会产生事件对象,并且系统会以实参的形式传给事件函数,所以需要形参接收。
<ul><li>123</li></ul>
<script>
var ul = document.querySelector('ul');
ul.addEventListener('click', function(e) {
// 我们给ul绑定了事件,那么this 就指向ul
console.log(this);//ul
// e.target 触发了事件的对象 我们点击的是li e.target 指向的就是li
console.log(e.target);//li
});
</script>
事件委托
-
概念: 也称事件代理,不给子元素注册事件,给父元素注册事件,把处理代码放在父元素的事件中执行。
-
事件委托的原理
给父元素注册事件,利用事件冒泡,当子元素的事件触发,会冒泡到父元素,然后去控制相应的子元素。
<ul>
<li>点击1</li>
<li>点击2</li>
<li>点击3</li>
</ul>
<script>
// 事件委托的核心原理:给父节点添加侦听器, 利用事件冒泡影响每一个子节点
var ul = document.querySelector('ul');
ul.addEventListener('click', function(e) {
// e.target 这个可以得到我们点击的对象li
e.target.style.backgroundColor = 'pink';
})
</script>
BOM对象
window对象
DOMContentLoaded: 事件触发时,仅当DOM加载完成,不包括样式表,图片等
document.addEventListener('DOMContentLoaded', function() {
alert(33);
})
window.onresize: 是调整窗口大小加载事件, 当触发时就调用的处理函数。
- 只要窗口大小发生像素变化,就会触发这个事件。
- 我们经常利用这个事件完成响应式布局。 window.innerWidth 当前屏幕的宽度
location对象
作用: location对象用于获取或设置窗体的URL,并且可以用于解析URL。
URL: 统一资源定位符是互联网上标准资源的地址。——路径
// URL一般的语法格式
// protocol://host[:post]/path/[?query]#fragment
https://study.163.com/course/courseLearn.htm?courseId=11111#/learn/live?lessonId=11111&courseId=11111
| 组成 | 说明 |
|---|---|
| protocol | 通信协议;常用的http、ftp、https等 |
| host | 主机(域名) |
| port | 端口号(可选),省略时使用默认端口 |
| path | 路径,由零或多个‘/'隔开的字符串,表示主机上的一个地址 |
| query | 参数,以键值对的形式,通过&隔开 |
| fragment | 片段,#后面内容创建于链接锚点 |
location对象的属性
| 属性 | 返回值 |
|---|---|
| href | 获取或者设置整个URL |
| host | 返回主机域名 |
| port | 返回端口号,没有返回空字符串 |
| pathname | 返回路径 |
| search | 返回参数 |
| hash | 返回片段 |
| location对象方法 | 返回值 |
|---|---|
| assign() | 跟href一样,可以跳转页面(也成为重定向页面) |
| replace() | 替换当前页面(无记录) |
| reload() | 重新加载页面(相当于按F5),参数为true时为强制刷新 |
navigator对象
navigator对象包含有关浏览器的信息,存在很多信息,常用的是 userAgent,该属性可以返回由客户机发送服务器的user-agent头部的值。
1.userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36" // PC
2.userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1" // mobile
示例: 判断用户那个终端打开页面,实现跳转
if((navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i){
window.location.href = ""; //移动端
} else {
window.location.href = ""; //电脑
}
history对象
window对象给我们提供了一个 history对象,与浏览器历史记录进行交互。该对象包含用户(在浏览器窗口中)访问过的URL。
| history对象方法 | 作用 |
|---|---|
| back() | 可以后退 |
| forward() | 前进 |
| go(参数) | 前进后退;参数是正整数前进n个页面;否则相反 |
元素位置
offset元素偏移
| offset系列属性 | 作用 |
|---|---|
| element.offsetParent | 返回该元素带有定位的父级元素,没有则返回body |
| element.offsetTop | 返回元素相对定位父元素上方的偏移 |
| element.offsetLeft | 返回元素相对定位父级左边框的偏移 |
| element.offsetWidth | 返回自身宽度包括padding、border、width; 返回数值无单位 |
| element.offsetHeight | 返回自身高度包括padding、border、height; 返回数值无单位 |
元素可视区 client
client 翻译过来就是客户端,我们使用 client 系列的相关属性来获取元素可视区的相关信息。通过 client系列的相关属性可以动态的得到该元素的边框大小、元素大小等。
- 属性说明
| client系列属性 | 作用 |
|---|---|
| element.clientTop | 返回元素上边框的大小 |
| element.clientLeft | 返回元素左边框的大小 |
| element.clientWidth | 返回自身包括padding、width,不含border,返回值不带单位 |
| element.clientHeight | 返回自身包括padding、height,不含border,返回值不带单位 |
元素滚动 scroll
| scroll系列属性值 | 作用 |
|---|---|
| element.scrollTop | 返回被卷去的上侧距离,返回数值不带单位 |
| element.scrollLeft | 返回被卷去的左侧距离,返回数值不带单位 |
| element.scrollWidth | 返回自身实际宽度,不含边框,返回数值不带单位 |
| element.scrollHeight | 返回自身实际高度,不含边框,返回数值不带单位 |
页面被卷去的头部兼容性解决方案
- 声明了 DTD,使用 document.documentElement.scrollTop
- 未声明 DTD,使用 document.body.scrollTop
- 新方法 window.pageYOffset和 window.pageXOffset,IE9 开始支持
function getScroll() {
return {
left: window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft||0,
top: window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0
};
}
//使用的时候 getScroll().left
\