http是无状态协议,服务器不记录客户端的状态,无法识别客户端。举个例子,假如你在牛客网上刷题,需要登录,你登录后,打开刷题的链接,但服务器无法识别你之前是否登录,你就要在这个刷题的页面再次登录,若要打开同一个网站的多个网页,就需要重复登录,这个过程很麻烦也很费时,所以就引入了cookie技术。cookie是以小的文本形式存在于客户端,cookie保存了登录的凭证,只要下次请求时带着cookie发送,不必重新输入用户名和密码重新登了。
cookie技术是在请求和响应报文中写入cookie信息来控制客户端的状态。当你首次浏览某网站时,这个网站会在发送报文中设置set-cookie的首部字段信息,在你的电脑上保存了cookie,这是一个小的文本文件,记录了你的用户名、密码、浏览的网络信息等,当你再次来到这个网站时,服务器会先看看有没有他上次留下来的
1.数据存储位置
都是存储在浏览器本地的,cookie是由服务器端写入的,sessionstorage、localstorage都是由前端写入的,
2.生命周期
cookie的生命周期是由服务器端在写入时候就设置好的,localStorage是写入就一直存在,除非手动清除。sessionstorage是页面关闭时候自动清除。
3.存储空间比较
cookie的存储空间大概4KB,SessionStorage、LocalStorage存储空间较大,大概有5M。
4.数据共享
cookie、sessionstorage、localstorage数据共享遵循同源原则,sessionStorage限制是同一个页面。
5.发送请求
前端发送给后端的请求中会自动携带cookie数据,但sessionStorage、LocalStorage不会。
6.应用场景
cookie一般用于存储登录验证信息SessionID或者token,localStorage常用于存储不易变动的数据,减轻服务器的压力,SessionStorage可以用来检测用户是否刷新进入页面。
1.客户端发送一个http请求到服务器端
2.服务器端发送一个http响应到客户端,其中包含Set-Cookie头部
3.客户端发送一个http请求到服务器端,其中包含Cookie头部
4.服务器端发送一个http响应到客户端
基本数据类型为:number、string、Boolean,Null,undefined,BigInt,Symbol七种。
引用数据类型:Object、Array、regexp.
2.存储方式不同
(1)基本数据类型是直接存储在栈中的简单数据段,占据空间小,属于被频繁使用的数据。
(2)引用数据类型是存储在堆内存中,占据空间大。引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址,当解释器寻找引用值时,会检索其在栈中的地址,取得地址后从堆中获得实体。
3.Symbol是ES6新出的一种数据类型
这种数据类型的特点是没有重复的数据,可以作为object的key。
4.BigInt也是ES6新出的一种数据类型,这种数据类型的特点就是数据涵盖的范围大,能够解决超出普通数据类型范围报错的问题。
1.理解闭包就要去理解变量的作用域,在js上有2种作用域,一种是全局变量,一种是局部变量。在函数内部可以读取全局变量,但在函数外部无法读取函数内部的局部变量。
闭包是指有权访问另一个函数作用域中的局部变量的函数。声明在一个函数中的函数,叫做闭包函数。内部函数可以访问其所在的外部函数中声明的参数和变量,即使在其外部函数被返回之后。
1.让外部访问函数内部成为了可能。
2.局部变量会常驻在内存中。
3.可以避免使用全局变量,防止全局变量污染。
4.会造成内存泄漏。
例1
var age = 10;
function foo(){
console.log(age);//-----------------1
var name = "hunt_bo";
return function(){
console.log(name);
}
}
console.log(name);//---------------2
var bar = foo();
bar();
在这个闭包的事例中,首先函数内部是可以访问全局变量的,在foo()函数中可以输出age的值为10,但从全局环境中是不能访问到函数中的name的。在用到闭包时,在foo函数内返回一个函数,在返回的函数中用到局部变量name,在外部调用时,例如bar(),可以使用到函数foo()中的属性name.
例2:
function addCount(){
var count = 0;
return function(){
count += 1;
console.log(count);
}
}
var fun1 = addCount();
var fun2 = addCount();
fun1();//1
fun1();//2
fun1();//3
fun2();//1
fun2();//2
每次外部函数执行的时候,外部函数的引用地址不同,都会重新创建一个新的地址。所以说虽然fun1和fun2都是addCount(),但是呢都创建了新地址,都是自己的,互不干扰。
promise是异步编程的解决方案,其实是一个构造函数,自身有all、reject、resolve这几个方法,可以进行异步操作。
promise有以下特点:
(1)promise对象不受外界的影响。promise对象有3种状态分别为:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作无法改变这个状态。
(2)promise对象改变的原因有2种,从pending变为fulfilled和从pending变为rejected。
promise对象可以避免层层嵌套的回调函数,而且提供了统一的接口,使得异步操作更加容易。
Promise对象是一个构造函数,用来生成promise实例。
创造一个promise实例
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
(1)Promise函数接收一个函数作为参数,该函数的两个参数分别是resolve和reject,他们是2个函数,有javascript引擎提供。
(2) 一旦状态改变,就不会再变,任何时候都可以得到这个结果。其中pending为初始状态,fulfilled和rejected为结束状态(结束状态表示promise的生命周期已结束),resolve函数的作用为将promise对象的状态从"pending"变为"fulfilled",在异步操作成功完成后,将异步操作的结果作为参数传递出去。reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
(3)可以使用promise的then方法来指定fulfilled状态和rejected状态的回调函数
promise.then(function(value) {
// success
}, function(error) {
// failure
});
then方法可以接受两个回调函数作为参数。第一个回调函数是Promise对象的状态变为resolved时调用,第二个回调函数是Promise对象的状态变为rejected时调用。这两个函数都是可选的,不一定要提供。它们都接受Promise对象传出的值作为参数。
下面是一个Promise对象的简单例子。
let promise = new Promise(function(resolve, reject) {
console.log('Promise');
resolve();
});
promise.then(function() {
console.log('resolved.');
});
console.log('Hi!');
// Promise
// Hi!
// resolved
上面代码中,Promise 新建后立即执行,所以首先输出的是Promise。然后,then方法指定的回调函数,将在当前脚本所有同步任务执行完才会执行,所以resolved最后输出。
同源是一种约定,是浏览器最核心的安全功能。同源策略会阻止一个域的js脚本与另一个域的内容进行交互。同源协议是指2个页面的URL具有相同的协议、域名、端口号。
当请求的URL的协议、域名、端口号三者之间任意一个与当前页面的URL不同即为跨域.
跨域限制的原因:浏览器为了保证网页的安全,出的同源协议策略。
(1)无法读取非同源网页的Cookie、LocalStorage、IndexedDB
(2)无法读取非同源网页的dom
(3)无法向非同源地址发送AJAX请求
使用 跨域资源共享CORS(Cross-Origin Response Sharing),基本思路是用自定义的HTTP头部允许浏览器和服务器相互了解,以确定请求或响应是成功还是失败。
对于GET或POST请求,发送请求报文时会有一个额外的头部叫origin,origin头部包含发送请求的页面的源(协议、域名、端口号),以便服务器确定是否为其提供服务。下面是一个origin头部的实例
Orgin:http://www.nczonline.net
如果服务器决定响应请求,那应该发送Access-Control-Allow-Origin头部,包含相同的源;或者如果资源是公开的,则就包含*,比如
Access-control-Allow-Origin:http://www.nczonline.net
或者:Access-Control-Allow-Origin:*
若没有以上头部,或有了但不匹配,则表明服务器不会响应浏览器的请求。否则,服务器就会响应这个请求。
res.setHeader(‘Access-Control-Allow-Origin’, ‘*’);
BFC (Block Formatting Context) 块格式化上下文,是web页面的一块独立渲染区域,内部元素的渲染不会影响边界以外的元素区域,外部的元素也不会影响BFC内部的元素。BFC属于文档中的普通流。
1.float的值不是none,float的值为:left,right或inherit。
2.position的值不是static或relative,是absolute或fixed。
3.display的值是inline-block,table-cell,flex,table-caption,inline-flex.
4.overflow的值不是visible,是hidden,auto或scroll。
当2个div的盒子竖直排列时,2个盒子中间的间距为1个margin,也就是60px,而不是120px,这就是margin的重合
<head>
<style>
.cube{
width:100px;
height:100px;
border:1px solid red;
margin:60px;
}
style>
head>
<body>
<div class="cube">div>
<div class="cube">div>
body>
我们可以看到2个div的盒子间的距离为1个margin为60px.为margin合并,
我们采用BFC来解决margin重合,分别设置2个container,让父容器达到BFC,将2个container的overflow设为hidden,
<head>
<style>
.cube{
width:100px;
height:100px;
border:1px solid red;
margin:60px;
}
.container{
overflow:hidden;
}
style>
head>
<body>
<div class="container">
<div class="cube">div>
div>
<div class="container">
<div class="cube">div>
div>
body>
父容器container中有一个子元素,让子元素达到左浮动,我们理想得到的是一个包含子容器的盒子,即一个红色的边框包含着蓝色的正方形。但实际得到的却是下图,这是因为子元素实现了左浮动,脱离了标准文档流。
<head>
<style>
.cube{
width:100px;
height:100px;
background:blue;
margin:60px;
float:left;
}
.container{
border:1px solid red;
}
style>
head>
<body>
<div class="container">
<div class="cube">div>
div>
body>
我们为父元素添加overflow:hidden,为父元素形成BFC,
<head>
<style>
.cube{
width:100px;
height:100px;
background:blue;
margin:60px;
float:left;
}
.container{
border:1px solid red;
overflow: hidden;
}
style>
head>
<body>
<div class="container">
<div class="cube">div>
div>
body>
当2个块元素因为一个元素的左浮动而导致2个元素相互重叠时,如下图:
<head>
<style>
.cube1{
width:100px;
height:100px;
background:blue;
float:left;
}
.cube2{
width:200px;
height:200px;
background:pink;
}
style>
head>
<body>
<div class="cube1"> div>
<div class="cube2">div>
body>
<head>
<style>
.cube1{
width:100px;
height:100px;
background:blue;
float:left;
}
.cube2{
width:200px;
height:200px;
background:pink;
overflow: hidden;
}
style>
head>
<body>
<div class="cube1"> div>
<div class="cube2">div>
body>
1.有4种方法判断变量的类型,分别是typeof、instanceof,Object.prototype.toString.call(),constructor。
typeof:用于判断基本数据类型typeof能判断7种,分别是undefined,boolean,string,number,object,function,symbol
let message="some ";
console.log(typeof(message)); //string
console.log(typeof(1)); //number
console.log(typeof("1")); //string
console.log(typeof(true)); //boolean
console.log(typeof({})); //object
console.log(typeof([])); //object
console.log(typeof(null)); //object
console.log(typeof(()=>{})); //function
console.log(typeof(undefined)); //undefined
console.log(typeof(Symbol(1))) //symbol
只要创建一个函数,就会为这个函数创建一个prototype属性,这个属性是个对象。所有的原型对象自动获得一个名为constructor的属性,指向其构造函数。对于下面的例子,Person.prototype.constructor指向Person。
constructor用于指向实例的构造函数
每个对象上有__proto__属性,用于对象实例来访问原型对象。
prototype是函数具有的属性,用于指向函数的原型对象。__proto__是对象具有的属性,用于指向对象的原型对象。
function Person(){}
console.log(Person.prototype);//Object 说明Person.prototype是一个Object对象
console.log(typeof Person.prototype);//object 说明原型对象的类型是object对象
console.log(Person.prototype.constructor===Person);//true 原型对象有一个属性是constructor,constructor指向原型对象的构造函数是Person
console.log(Person.prototype===Object); //false 这里之所以是false,因为这里的Object表示的是构造函数,但Person.prototype表示的是对象,所以为false
console.log(Person.prototype.__proto__===Object.prototype);//true 原型链都会终止于Object函数的原型对象
console.log(Person.prototype.__proto__.constructor===Object); //true 因为Person.prototype.__proto__相当于Object.prototype。Object函数的原型对象的constructor为Object函数
console.log(Person.prototype.__proto__.__proto__===null); //Object的原型的原型为null
console.log(Person.prototype.__proto__); //为Object对象
实例通过__proto__链接到构造函数的原型对象,
构造函数通过prototype属性链接到原型对象
实例通过__proto__链接到构造函数的原型对象,再通过constructor链接到构造函数
function Person(){}
let person1=new Person();
let person2=new Person();
console.log(person1.__proto__===Person.prototype); //true
console.log(person1.__proto__.constructor===Person); //true
由以上信息可以得到下图:
Person.prototype.constructor=Person;//1
Object.prototype.constructor=Object;//2
Person.prototype.__proto__=Object.prototype;//3//这条很重要
Object.prototype.__proto__=null;//4
用Person.prototype.__proto__来表示Object.prototype代入到2式和4式中,可以推导得到:
Person.prototype.__proto__.constructor=Object
Person.prototype.__proto__.__proto__=null
function Person(){}
let person1=new Person();
let person2=new Person();
console.log(person1.__proto__===Person.prototype); //true
console.log(person1.__proto__.constructor===Person); //true
同一个构造函数构造的2个实例共享1个原型对象
function Person(){}
let person1=new Person();
let person2=new Person();
console.log(person1.__proto__===person2.__proto__); //true
instanceof:用于区分引用数据类型,检测方法是构造函数的原型对象是否在在当前实例的原型链上,用instanceof检测出来的结果都是true,不太适合用于简单数据类型的检测,对于简单数据类型的undefined,null,symbol检测不出来。
例1:instanceof分别检验构造函数Person的prototype属性是否在person1这个实例的原型链上。
例2: instanceof分别检验构造函数Object的prototype属性是否在person1这个实例的原型链上。
function Person(){}
let person1=new Person();
//在person1的原型链上找构造函数,原型链终止于Object.prototype
console.log(person1 instanceof Person); //true 因为person1.__proto__为Person.prototype
console.log(person1 instanceof Object); //true 因为person1.__proto__为Person.prototype,Person.prototype.__proto__为Object.prototype,也就是person1.__proto__.__proto__为Object.prototype
console.log(Person.prototype instanceof Object); //true 因为Person.prototype.__proto__为Object.prototype
person1,person2对象既是Object实例,也是Person的实例
function Person(name,age,job){
this.name=name;
this.age=age;
this.job=job;
}
let person1=new Person("Nichelous",29,"software");
let person2=new Person("Greg",13,"Doctor");
console.log(person1 instanceof Object);//true
console.log(person1 instanceof Person);//true
console.log(person2 instanceof Object);//true
console.log(person2 instanceof Person);//true
console.log( ({}) instanceof Object);//true
console.log( [] instanceof Array);//true
明白了instanceof的功能和原理后,可以自己实现一个instanceof同样功能的函数:
原型链其实就是一个类似链表的数据结构。
instanceof做的事,就是在链表上寻找有没有目标节点。从表头节点开始,不断向后遍历,若找到目标节点,返回true。遍历结束还没找到,返回false。
function Person(){}
function Person5(){}
let person1=new Person();
function myinstanceof(obj,constructor){
let objPrototype=obj.__proto__; //创建实例的原型对象
let conPrototype=constructor.prototype;//创建构造函数的原型对象
while(objPrototype!=null) //原型链会终止于null
{
if(objPrototype==conPrototype) //若找到,返回true
return true;
objPrototype=objPrototype.__proto__; //继续遍历原型链
}
return false;
}
console.log(myinstanceof(person1,Person)); //true
console.log(myinstanceof(person1,Object)); //true
console.log(myinstanceof(person1,Person5)); //false
用于检测引用数据类型,检测方法为获取实例的构造函数与某个类是否相同,如果相同就说明该数据是符合那个数据类型的,这种方法不会把原型链上的其他类加入进来,避免干扰。
console.log((1).constructor === Number); // true
console.log(''.constructor === String); // true
console.log(false.constructor === Boolean); // true
console.log([].constructor === Array); // true
console.log(({}).constructor === Object); // true
console.log((function foo() {}).constructor === Function);// true
console.log(Symbol('1').constructor === Symbol);// true
//能判断如下类型 : Number , String , Boolean , Symbol , Function , Object , Array ; null 和 undefined没有constructor属性,所以不能判断
Object.prototype.stoString.call()返回的是该数据类型的字符串,各种数据类型都能检测且检测精准的就是Object.prototype.toString.call()这种方法.
let obj = {}
(1) Object.defineProperty(obj,Symbol.toStringTag,{
get:function(){
return 'hello'
}
})
(2) Object.prototype[Symbol.toStringTag] = 'hello'
console.log(Object.prototype.toString.call(obj)) //[object,hello]
function Foo(){};
const foo = new Foo();
Object.prototype.toString.call(1); // '[object Number]'
Object.prototype.toString.call('1'); // '[object String]'
Object.prototype.toString.call(true); // '[object Boolean]'
Object.prototype.toString.call(undefined); // '[object Undefined]'
Object.prototype.toString.call(null); // '[object Null]'
Object.prototype.toString.call(Symbol(1)); // '[object Symbol]'
Object.prototype.toString.call(Foo); // '[object Function]'
Object.prototype.toString.call(foo); // '[object Object]' , 实例化对象属于Object
Object.prototype.toString.call({}); // '[object Object]'
Object.prototype.toString.call([]); // '[object Array]'
Object.prototype.toString.call(NaN); // '[object Number]' ,此方法判断NaN也属于Number
!important>行内样式>嵌入样式>外链样式>id选择器>类选择器>标签选择器>复合选择器>通配符>继承样式
CSS样式的优先级应该分成四大类
第一类:
!important的优先级最高,
如果a标签不加!important特性,那么显示的颜色为蓝色,加了!important之后优先级提高了,显示颜色为绿色。
<style>
#main a{
color:blue;
}
a{
color:green !important;
}
style>
head>
<body>
<div id="main">
<a>实例a>
div>
在下面的代码中,box的所有div中字体为红色,我们用class定义了ikun1,ikun2自身的颜色为蓝色,ikun1未使用!important,ikun2使用了!important。
因为class的优先级小于id,所以ikun1即使使用了类来定义自身属性,也无法生效,颜色为红色。但ikun2使用了!important来提升优先级,所以ikun2的颜色为蓝色。
<head>
<style>
#box div{
color:red;
}
.ikun1{
color:blue;
}
.ikun2{
color:blue !important;
}
style>
head>
<body>
<div id="box">
<div class="ikun1">ikun1div>
<div class="ikun2">ikun2div>
div>
body>
第二类引入方式
行内样式>内嵌样式>外链样式
由以下代码可知,ikun1的颜色为红色,ikun2,和ikun3的颜色为蓝色,即行内样式>内嵌样式。
<head>
<style>
div{
color:blue
}
style>
head>
<body>
<div id="box">
<div style="color:red">ikun1div>
<div>ikun2div>
<div>ikun3div>
div>
body>
第三类:
选择器的优先级:
id选择器>(类选择器|伪类选择器|属性选择器)>(后代选择器|伪元素选择器)>(子选择器|相邻选择器)>通配符选择器。
第四类:
样式的继承:我们为一个元素设置的样式同时也会被应用到它的后代元素上,继承是发生在祖先后代之间的。
继承样式的优先级是所有样式中优先级较低的。
例如如下代码,我们给box设置字体颜色为蓝色,box的子元素ikun1,ikun2就继承了box的字体颜色属性。
<head>
<style>
div{
color:blue
}
style>
head>
<body>
<div id="box">
<div style="color:red">ikun1div>
<div>ikun2div>
<div>ikun3div>
div>
body>
实现异步的方法有:
回调函数、事件监听、setTimeout、Promise、生成器Generators/yield、async/awt
同步
指在主线程排队执行的任务,只有前一个任务执行完毕后,后一个任务才能继续执行。
调用一旦开始,这个调用必须返回结果,才能继续向下执行。程序执行的顺序与任务排列顺序是一致的。
异步
异步任务不进入主线程,而进入任务队列,只有任务队列通知主线程,某个异步任务可以进行了,该任务才会进入主线程。
每一个任务有一个或多个回调函数。前一个任务结束后,不是执行后一个任务,而是执行回调函数,后一个任务不等前一个任务结束就执行。
程序的执行顺序与任务的排列顺序是不一致的,异步的。
我们常用的setTimeout和setInterval函数,Ajax都是异步操作。
把函数A当做参数传到另一函数B中,当需要这个函数A时,再调用这个A函数,这个A函数称为回调函数。
function add(num1,num2,callback){
var sum=num1+num2;
callback(sum);
}
function print(num){
console.log(num);
}
add(5,6,print);
分析:在add(5,6,print)中,函数print作为参数传入到add函数中,但并不是马上起作用,而是在var sum=num1+num2;运行完成后需要打印输出sum时才会调用这个函数。
1.不会立即执行,回调函数作为参数传递给一个函数时,传递的是参数的定义并不会立即执行。回调函数在调用函数中要通过()运算符才会执行。
2.回调函数是一个闭包,也就是说它能访问到其外层定义的变量。
正常this指向问题:
什么是this:自动引用正在调用当前方法的对象。
this指向的三种情况:
再来看回调函数的this指向问题
function create(callback){
callback();
}
var obj={
data:100,
total:function(){
create(function(){
console.log(this); //window
});
}
}
obj.total();
结果:window
回调函数中的this默认指向window,因为本质上回调函数callback并没有当前对象的调用。
解决方法:使用箭头函数,箭头函数根本没有自己的this,导致内部的this就是外层代码的this。因为它没有this,所以不能用作构造函数。
function create(callback){
callback();
}
var obj={
data:100,
total:function(){
create(()=>{
console.log(this); //window
});
}
}
obj.total();
结果:{data: 100, total: ƒ}
输出的就是obj对象。
Vue使用Object.defineProperty()来实现数据的双向绑定。
首先来看这个函数Object.defineProperty(),这个方法接收3个参数:
1.属性所在的对象
2.属性的名字
3.一个描述符对象
描述符对象的属性可以包含:configurable,enumerable,writable,value。
1.configurable:表示能否通过delete删除属性,能否修改属性的特性,默认值为false
2.enumerable:表示能否通过for-in循环访问属性,默认值为false
3.writable:表示能否修改属性值,默认为false
4.value:这个属性的数据值,默认为undefined。
let person={};
Object.defineProperty(person,"name",{
writable:false,
value:"nicholas"
});
console.log(person.name); //nicholas
person.name="bob";
console.log(person.name); //nicholas
我们可以看到2个输出的都是Nicholas,name属性的值是无法更改的。
let p1={};
Object.defineProperty(p1,"name",{
configurable : false,
value:"lisi"
})
console.log(p1); //{ name: 'lisi' }
delete p1.name;
console.log(p1); //{ name: 'lisi' }
我们可以看到属性是无法删除掉的。
Object.defineProperty()的第三个属性除了可以是数据属性,也可以是访问器属性。
访问器属性包含获取(getter)函数和设置(setter)函数,
get:获取函数,在读取属性时调用。默认值undefined。
set:设置函数,在设置属性时调用。默认值undefined。
let cai={
name:"kunkun",
ikun:120,
love:"basketball"
}
Object.defineProperty(cai,"fans",{
get(){
return this.ikun; //返回的是ikun的属性的值
},
set(newValue){ //当fans属性更新时,传入的参数是newValue
this.ikun=newValue; //让ikun属性随着fans属性的更新而更新
}
});
console.log(cai.ikun); //120
console.log(cai.fans); //120 返回的就是this.ikun
cai.fans=200;
console.log(cai.ikun); //200 当fans属性更新时,ikun属性也随之更新
cai.ikun=300;
console.log(cai.fans);//300
代码实现了fans属性与ikun属性的双向绑定,fans属性返回的值就是ikun的数据值,当ikun属性的值发生改变时,fans属性的值也发生了改变。当fans属性的值发生改变时,ikun属性的值也发生改变。
Vue响应式指的是:组件的data发生变化,立刻触发试图的更新
原理: Vue 采用数据劫持结合发布者-订阅者模式的方式来实现数据的响应式,通过Object.defineProperty来劫持数据的setter,getter,在数据变动时发布消息给订阅者,订阅者收到消息后进行相应的处理。
通过原生js提供的监听数据的API,当数据发生变化的时候,在回调函数中修改dom,使用Object.defineProperty API, 获取属性值会触发getter方法,设置属性值会触发setter方法,在setter方法中调用修改dom的方法 。
Object.defineProperty的缺点:
实现数据的双向绑定代码:
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"
/>
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Documenttitle>
<style>
#myInput {
width: 400px;
height: 50px;
font-size: 40px;
color: red;
}
#contain {
margin-top: 20px;
width: 400px;
height: 200px;
border: 1px solid salmon;
}
style>
head>
<body>
<input id="myInput" type="text" />
<div id="contain">div>
<script>
var text;
window.data = {};
var oIn = document.getElementById("myInput");
var oDiv = document.getElementById("contain");
oIn.addEventListener("input", function (e) {
text = e.target.value;
console.log(text);
window.data.value = text;
});
Object.defineProperty(window.data, "value", {
get() {
return "";
},
set(v) {
oDiv.innerHTML = v;
},
});
script>
body>
html>
undefined类型只有一个值就是特殊值undefined,
使用var或let声明变量但没有初始化时
或者声明了变量并赋予初值为undefined
或者某个对象不存在某个属性却去访问
或者函数定义了形参却没有穿实参。
使用typeof判断undefined的类型为undefined。
console.log(typeof(undefined)); //undefined
let message;
console.log(message===undefined);//true
let ikun=undefined;
console.log(ikun===undefined);//true
console.log(undefined==undefined);//true
console.log(undefined===undefined);//true
null表示一个对象没有设置初始值,相当于一个对象没有设置指针地址,相当于空对象指针。
使用typeof来判断null的类型为object
console.log(typeof null); object
console.log(null==undefined);//true
console.log(null===undefined);//false
let car=null;
console.log(car);
undefined是由null派生而来的,用==来比较undefined和null时会返回true。
null 其实属于自己的类型 Null,而不属于Object类型,typeof 之所以会判定为 Object 类型,是因为JavaScript 数据类型在底层都是以二进制的形式表示的,二进制的前三位为 0 会被 typeof 判断为对象类型,而 null 的二进制位恰好都是 0 ,因此,null 被误判断为 Object 类型。
对象被赋值了null 以后,对象对应的堆内存中的值就是游离状态了,GC 选择机回收该值并释放内存。因此,需要释放某个对象,就将变量设置为 null,即表示该对象已经被清空,目前无效状态。
1.可以实现文字环绕盒模型:
<head>
<style>
.one {
width: 300px;
height: 300px;
}
.one div {
float: left;
height:100px;
width:100px;
border:1px solid red;
}
style>
head>
<body>
<div class="one"><div>div>
<p>黑白棋该怎么玩呢?不妨一起来看看吧!希望能给大家带来帮助! 一般黑白棋棋盘共有8行8列共64格。 开局时,棋盘正中央的四格先放置黑白相隔的四枚棋子,双方轮流落子。 只要落子和棋盘上任一枚己方棋子在一条线上夹着对方棋子,就可将对方的这些棋子转变为我己方。 如果在任意位置落子都不能夹住对方的任一颗棋子,就要让对手下子。
p>
div>
<script>
script>
body>
2.设置了浮动的行内元素可以设置宽高
例:
<head>
<style>
label{
/*float:left;*/
width:100px;
height:200px;
border:1px solid red;
}
style>
head>
<body>
<label>行内元素的宽高随标签里的内容而变化。label>
<script>
script>
body>
当行内元素没有设置浮动时,没有宽高,如下图:
当在label标签中设置浮动时,行内元素有了宽高,如下图:
3.可以按照元素浮动设置方向对齐排列盒子模型,例如左浮动或右浮动。
设置了浮动,该元素脱离了标准文档流,如果父级盒子没有设置高度,子元素设置为浮动,那么父级盒子会被子盒子撑开,这时父级盒子的高度就塌陷了。如果浮动元素后面还有兄弟盒子,那么兄弟盒子的布局也会被影响。
例以下为2个兄弟元素当son1没有设置左浮动时:
<head>
<style>
.son1{
width:100px;
height:200px;
border:1px solid red;
}
.son2{
width:100px;
height:200px;
border:5px solid green;
}
style>
head>
<body>
<div class="son1">div>
<div class="son2">div>
如图:
当给son1加了左浮动时,对son2元素的影响
2个兄弟元素重合到了一起。
给父级元素添加伪元素
通过给父级元素条件伪元素,来实现解决父级盒子塌陷的问题:
<head>
<style>
.son1{
float:left;
width:100px;
height:200px;
border:1px solid red;
}
.father::after{
display: block; /* 使其成为块级元素后*/
content: "";/*为伪元素加入空内容,以便伪元素中不会有内容显示在页面中*/
height: 0;
clear: both; /* 清除浮动*/
}
.father{
*zoom:1;
border:1px solid green;
}
style>
head>
<body>
<div class="father">
<div class="son1">div>
<div class="son2">div>
div>
<script>
script>
body>
使用伪元素清除浮动,不会新增标签,不会有其他影响,是当下清除浮动最流行的方法。
2. 使用bfc,给父级元素添加overflow:hidden,不会新增标签,但是父级元素有定位元素超出父级,超出部分会隐藏,在不涉及父级元素有超出内容的情况,overflow:hidden常用。
箭头函数的实例与函数表达式的创建是相同的。
let arrowSum=(a,b)=>{
return a+b;
};
let expression=function(a,b){
return a+b;
}
console.log(arrowSum(5,10));//15
console.log(expression(5,10));//15
当有一个参数时,不用括号,只有没有参数或有多个参数时,使用括号。
let triple=x=>{return 3*x;};
let getRandom=()=>{return 2;};
let sum=(a,b)=>{return a+b}
let obj={
ikun1:()=>{
console.log(this);
},
ikun2:function(){
console.log(this);
}
}
obj.ikun1();//Window {window: Window, self: Window, document: document, name: '', location: Location, …}
obj.ikun2();//{ikun1: ƒ, ikun2: ƒ}
从以上代码可得出,在对象obj中的箭头函数ikun1中,输出的this不是当前的对象obj,而是obj的外部window,但在ikun2中,ikun2是正常function定义的函数,所以ikun2输出的this为obj。
2. 箭头函数没有原型和super,不能使用arguments,new.target.
简单函数表达式,内部没有this引用,没有递归、事件绑定,解绑定。
函数名就是指向函数的指针,所以意味着一个函数可以有多个名称。
function ikun1(num1,num2){
console.log(num1+num2);
}
ikun1(10,10);//20
let ikun2=ikun1; //声明变量ikun2,初始化为ikun1
ikun2(10,10);//20
ikun1=null;
ikun2(20,20);//40
以上代码定义了一个函数ikun1,又声明了变量ikun2让其值等于ikun1,使用不带括号的函数名会访问函数指针,而不会执行函数。此时,ikun1和ikun2都指向同一个函数,调用ikun2也可以返回结果,将ikun1设置为null,切断了ikun1与函数的联系。ikun2可以正常使用。
当定义函数时,行参设置为传入2参数,但不意味着实际传入参数时就传2个参数,你可以传入1个、2个、3个、n个,或一个都不传。
之所以这样,因为函数的参数在内部表现为一个数组。函数被调用时会接收一个数组,函数并不关心这个数组中有什么,如果数组中什么也没有,那没问题;如果数组中元素超出了要求,那也没问题。
在使用function定义的函数中,可以在函数内部访问arguments对象,从中取得每个传进来的参数值。
arguments对象是一个类数组对象,但不是Array实例,可以使用arguments[i]来访问第i个传入函数的参数。若要确定穿进来的参数的个数,使用arguments.length
function ikun(){
console.log(arguments[0]+" "+arguments[1]);
}
ikun("hello","kitty");//hello kitty
通过arguments.length来确定传入到函数的参数的个数。
function ikun(){
console.log(arguments.length);
}
ikun("hello","kitty","f");//3
ikun();//0
ikun(12);//1
函数名就是变量,所以函数可以用在任何可以使用变量的地方。这意味着不仅可以把函数作为参数传给另一个函数,还可以在另一个函数中返回另一个函数。
function playIkun(play,ikun){
play(ikun);
}
function add(num){
return num+10;
}
function greet(name){
return "hello"+name;
}
let result=playIkun(add,10);//20
let result2=playIkun(greet,"bob");//"hello bob"
例1:
var name="小王",age=17;
var obj={
name:"小张",
objAge:this.age, //这个this指向的是window
myFun:function(){
console.log(this.name+"年龄"+this.age);
}
};
console.log(obj.objAge);//17
obj.myFun();//小张年龄undefined
例2:
var fav="盲僧";
function shows(){
console.log(this.fav);
}
shows();//盲僧
在例1中,obj.myFun()中输出的this指向的obj,例2中全局声明的shows()函数this是window。
var obj={
name:"小张",
objAge:this.age, //这个this指向的是window
myFun:function(){
console.log(this.name+"年龄"+this.age);
}
};
var db={
name:"德玛",
age:99
};
obj.myFun.call(db);//德玛年龄99
obj.myFun.apply(db);//德玛年龄99
obj.myFun.bind(db)();//德玛年龄99
以上例子是将obj的myFun函数的this对象进行改变为db对象。
call(),apply(),bind()函数都是将选定函数的this对象进行改变。
var obj={
name:"小张",
objAge:this.age, //这个this指向的是window
myFun:function(fm,t){
console.log(this.name+"年龄"+this.age,"来自"+fm+"去往"+t);
}
};
var db={
name:"德玛",
age:99
};
obj.myFun.call(db,"成都","上海");//德玛年龄99 来自成都去往上海
obj.myFun.apply(db,["成都","上海"]);//德玛年龄99 来自成都去往上海
obj.myFun.bind(db,["成都","上海"])();//德玛年龄99 来自成都,上海去往undefined
从上面结果可以看出,call的参数是直接放进去的,第二、第三、第n个参数全都用逗号分隔。
apply的所有参数都必须放入到一个数组中传入进去。
bind除了返回的是函数外,它的参数和call一样。
HTML语义化指在不了解HTML的内容,就可以直到这个标签代表什么意义。
例子:
我们就不知道代表什么
无序列表
有序列表
语义化作用
- 易于用户阅读,样式文件未加载时,页面结构清晰。
- 利于SEO,搜索引擎根据标签来确定上下文和各个关键字的权重。
- 方便屏幕阅读器解析,如盲人阅读器根据语义渲染网页。
- 有利于开发和维护,代码更具可读性,代码更好维护。