一、HTML+CSS
1. 如何理解HTML的语义化
- 让人更容易读懂(增加代码可读性)。
- 让搜索引擎更容易读懂,有助于爬虫抓取更多的有效信息,爬虫依赖于标签来确定上下文和各个关键字的权重(SEO)。
- 在没有 CSS 样式下,页面也能呈现出很好地内容结构、代码结构。
2. 从浏览器地址栏输入url回车会发生什么
- 输入 URL 后解析出协议、主机、端口、路径等信息,并构造一个 HTTP 请求。
- DNS 域名解析。
- TCP 连接。
- http 请求。
- 服务器处理请求并返回 HTTP 报文。
- 浏览器渲染页面
- 断开 TCP 连接。
3. 溢出的文字显示省略号
- 单行
- 先强制一行显示
white-space:noweap - 超出的部分隐藏
overflow:hidden - 超出的部分用省略号显示
text-overflow:ellipsis
- 多行
overflow:hidden;
text-overflow:ellipsis;
display: -webkit-box;弹性伸缩盒子模型显示
-webkit-line-clamp:2;//限制在一个块元素显示的文本行数
-webkit-box-orient:vertical;//设置或检索伸缩对象子元素排序
4. css选择器和优先级
大家都知道样式的优先级一般为 !important > style > id > class
css选择器
| 选择器 | 示例 |
|---|---|
| 类型选择器 | h1 { } |
| 通配符选择器 | * { } |
| 类选择器 | .box { } |
| ID选择器 | #unique { } |
| 标签属性选择器 | a[title] { } |
| 伪类选择器 | p:first-child { } |
| 伪类选择器 | p::first-line { } |
| 后代选择器 | article p |
| 子代选择器 | article > p |
| 相邻兄弟选择器 | h1 + p |
| 通用兄弟选择器选择器 | h1 ~ p |
5. BFC
BFC 即块级格式上下文,这是一个独立渲染的区域,规定了内部如何布局,并且这个区域的子元素不会影响到外面的元素,其中比较重要的布局规则有内部box垂直放置,计算BFC高度的时候,浮动元素也参与计算。 BFC 具有一些特性:
- 块级元素会在垂直方向一个接一个的排列,和文档流的排列方式一致。
- 在 BFC 中上下相邻的两个容器的
margin会重叠,创建新的 BFC 可以避免外边距重叠。 - 计算 BFC 的高度时,需要计算浮动元素的高度,可以避免高度塌陷。
- BFC 区域不会与浮动的容器发生重叠。
- 每个元素的左
margin值和容器的左border相接触。
- 利用
4和5,我们可以实现三栏(或两栏)自适应布局。
创建 BFC 的方式:
- 绝对定位元素(
position为absolute或fixed)。 display为inline-block; table-cell; table-caption; flex; inline-flex。overflow的值不为visible。
6. 水平垂直居中的方法
- 利用绝对定位,设置
left: 50%和top: 50%现将子元素左上角移到父元素中心位置,然后再通过translate来调整子元素的中心点到父元素的中心。 - 利用绝对定位,子元素所有方向都为
0,将margin设置为auto,由于宽高固定,对应方向实现平分,该方法必须盒子有宽高。 - 利用绝对定位,设置
left: 50%和top: 50%现将子元素左上角移到父元素中心位置,然后再通过margin-left和margin-top以子元素自己的一半宽高进行负值赋值。该方法必须定宽高。
二、JavaScript
1. 基本数据类型
在 JS 中共有 8 种基础的数据类型,分别为: Undefined 、 Null 、 Boolean 、 Number 、 String 、 Object 、 Symbol(ES6新增,表示独一无二的值,最大的用法是用来定义对象的唯一属性名。) 、 BigInt(表示任意大小的整数)。
1.1 值类型和引用型数据的理解
- 值类型是直接存储在栈中的简单数据段,占据空间小、大小固定,属于被频繁使用数据,所以放入栈中存储;
- 引用类型存储在堆中的对象,占据空间大、大小不固定。如果存储在栈中,将会影响程序运行的性能;
1.2 数据类型的判断
- typeof:能判断所有值类型,函数。不可以对 null、对象、数组进行精确判断,因为都返回
object。
console.log(typeof undefined); // undefined
console.log(typeof 2); // number
console.log(typeof true); // boolean
console.log(typeof "str"); // string
console.log(typeof Symbol("foo")); // symbol
console.log(typeof 2172141653n); // bigint
console.log(typeof function () {}); // function
// 不能判别
console.log(typeof []); // object
console.log(typeof {}); // object
console.log(typeof null); // object
- instanceof:能判断对象类型,不能判断基本数据类型,其内部运行机制是判断在其原型链中能否找到该类型的原型。
- Object.prototype.toString.call() :所有原始数据类型都是能判断的,还有 Error 对象,Date 对象等。
Object.prototype.toString.call(2); // "[object Number]"
Object.prototype.toString.call(""); // "[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(Math); // "[object Math]"
Object.prototype.toString.call({}); // "[object Object]"
Object.prototype.toString.call([]); // "[object Array]"
Object.prototype.toString.call(function () {}); // "[object Function]"
1.3 如何判断数组
Array.isArray(arr); // true
arr.__proto__ === Array.prototype; // true
arr instanceof Array; // true
Object.prototype.toString.call(arr); // "[object Array]"
2. 数组的使用
数组API
一、数组序列化
- toString() 在默认情况下都会以逗号分隔字符串的形式返回数组项
- join() 使用指定的字符串用来分隔数组字符串
var arr = [1,5,2,8,10,{a:1}];
console.log(arr);//[ 1, 5, 2, 8, 10, { a: 1 } ]
console.log(arr.toString());//”1,5,2,8,10,[object Object]”
console.log(arr.join(""));//”152810[object Object]”
console.log(arr.join("-"));//”1-5-2-8-10-[object Object]”
二、栈和队列方法
- push() 方法可向数组的末尾添加一个或多个元素,并返回新的长度。
- pop() 方法用于删除数组的最后一个元素并返回删除的元素。
- shift() 方法用于把数组的第一个元素从其中删除,并返回第一个元素的值
- unshift() 方法可向数组的开头添加一个或更多元素,并返回新的长度。
三、排列方法
- reverse() 方法用于颠倒数组中元素的顺序。
- sort() 方法用于对数组的元素进行排序。
// 1. 翻转数组
var arr = ['pink', 'red', 'blue'];
arr.reverse();
console.log(arr);
// 2. 数组排序(冒泡排序)
var arr1 = [13, 4, 77, 1, 7];
arr1.sort(function(a, b) {
// return a - b; 升序的顺序排列
return b - a; // 降序的顺序排列
});
console.log(arr1);
四、操作方法
- concat() 方法用于连接两个或多个数组。
- substr()方法用于截取指定的一段字符
- slice() 方法可从已有的数组中返回选定的元素。
- splice() 方法用于添加或删除数组中的元素。
var str = 'andy';
console.log(str.concat('red'));//andyred
// 2. substr('截取的起始位置', '截取几个字符');
var str1 = '改革春风吹满地';
console.log(str1.substr(2, 2)); // 春风
五、位置方法
- indexOf() 方法可返回数组中某个指定的元素位置,找不到则返回-1。
- lastIndexOf() 方法可返回一个指定的元素在数组中最后出现的位置,从该字符串的后面向前查找。
var arr = ['red', 'green', 'pink'];
console.log(arr.indexOf('blue'));//-1
// 返回数组元素索引号方法 lastIndexOf(数组元素) 作用就是返回该数组元素的索引号 从后面开始查找
var arr = ['red', 'green', 'blue', 'pink', 'blue'];
console.log(arr.lastIndexOf('blue')); // 4
用indexOf()实现数组去重
function unique(arr) {
var newArr = [];
for (var i = 0; i < arr.length; i++) {
if (newArr.indexOf(arr[i]) === -1) {
newArr.push(arr[i]);
}
}
return newArr;
}
var demo = unique(['blue', 'green', 'blue'])
console.log(demo);
六、迭代方法
- every() 方法用于检测数组所有元素是否都符合指定条件(通过函数提供)。
- some() 方法用于检测数组中的元素是否满足指定条件(函数提供)。
- filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。
- map() 方法返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值。
- forEach() 方法用于调用数组的每个元素,并将元素传递给回调函数。
2.1 利用函数求任意个数的最大值
function getMax() {
var max = arguments[0];
for (var i = 1; i < arguments.length; i++) {
if (arguments[i] > max) {
max = arguments[i];
}
}
return max;
}
console.log(getMax(1, 2, 3));
2.2 利用函数翻转任意数组 reverse 翻转
function reverse(arr) {
var newArr = [];
for (var i = arr.length - 1; i >= 0; i--) {
newArr[newArr.length] = arr[i];
}
return newArr;
}
var arr1 = reverse([1, 3, 4, 6, 9]);
console.log(arr1);
var arr2 = reverse(['red', 'pink', 'blue']);
console.log(arr2);
2.3 利用函数冒泡排序 sort 排序
function sort(arr) {
for (var i = 0; i < arr.length - 1; i++) {
for (var j = 0; j < arr.length - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
var temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
return arr;
}
var arr1 = sort([1, 4, 2, 9]);
console.log(arr1);
2.4 利用函数判断闰年
function isRunYear(year) {
// 如果是闰年我们返回 true 否则 返回 false
var flag = false;
if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) {
flag = true;
}
return flag;
}
console.log(isRunYear(2000));
2.5 利用函数判断闰年
function backDay() {
var year = prompt('请您输入年份:');
if (isRunYear(year)) { // 调用函数需要加小括号
alert('当前年份是闰年2月份有29天');
} else {
alert('当前年份是平年2月份有28天');
}
}
backDay();
// 判断是否为闰年的函数
function isRunYear(year) {
// 如果是闰年我们返回 true 否则 返回 false
var flag = false;
if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) {
flag = true;
}
return flag;
}
2.6 利用set方法数组去重
function unique(arr){
var arr2 = arr.sort();
var res = [arr2[0]];
for(var i=1;i<arr2.length;i++){
if(arr2[i] !== res[res.length-1]){
res.push(arr2[i]);
}
}
return res;
}
//利用下标查询
function unique(arr){
var newArr = [arr[0]];
for(var i=1;i<arr.length;i++){
if(newArr.indexOf(arr[i]) == -1){
newArr.push(arr[i]);
}
}
return newArr;
}
3. 作用域和作用域链
- 作用域 :就是代码名字(变量)在某个范围内起作用和效果 目的是为了提高程序的可靠性更重要的是减少命名冲突
- js的作用域(es6)之前 : 全局作用域 局部作用域
- 全局作用域: 整个script标签 或者是一个单独的js文件
- 局部作用域(函数作用域) 在函数内部就是局部作用域 这个代码的名字只在函数内部起效果和作用
- 作用域链 : 内部函数访问外部函数的变量,采取的是链式查找的方式来决定取那个值 这种结构我们称为作用域链 就近原则 案例:
// 案例1 : 结果是几?
function f1() {
var num = 123;
function f2() {
var num = 0;
console.log(num); //num=0 站在目标出发,一层一层的往外查找
}
f2();
}
var num = 456;
f1();
// 案例2 :结果是几?
var a = 1;
function fn1() {
var a = 2;
var b = '22';
fn2();
function fn2() {
var a = 3;
fn3();
function fn3() {
var a = 4;
console.log(a); //a=4
console.log(b); //b=22
}
}
}
fn1();
3.1 预解析
- 我们js引擎运行js 分为预解析、代码执行
- 预解析 js引擎会把js 里面所有的 var 还有 function 提升到当前作用域的最前面
- 代码执行 按照代码书写的顺序从上往下执行
- 预解析分为 变量预解析(变量提升) 和 函数预解析(函数提升)
- 变量提升就是把所有的变量声明提升到当前的作用域最前面 不提升赋值操作
- 函数提升就是把所有的函数声明提升到当前作用域的最前面 不调用函数 案例:
var num = 10;
fun();
function fun() {
console.log(num);
var num = 20;
}
// 相当于执行了以下操作
var num;
function fun() {
var num;
console.log(num);
num = 20;
}
num = 10;
fun();
// // 案例2
var num = 10;
function fn() {
console.log(num);
var num = 20;
console.log(num);
}
fn();
// // 相当于以下代码
var num;
function fn() {
var num;
console.log(num);
num = 20;
console.log(num);
}
num = 10;
fn();
// // 案例3
var a = 18;
f1();
function f1() {
var b = 9;
console.log(a);
console.log(b);
var a = '123';
}
// 相当于以下代码
var a;
function f1() {
var b;
var a;
b = 9;
console.log(a);
console.log(b);
a = '123';
}
a = 18;
f1();
// 案例4
f1();
console.log(c);
console.log(b);
console.log(a);
function f1() {
var a = b = c = 9;
console.log(a);
console.log(b);
console.log(c);
}
// 以下代码
function f1() {
var a;
a = b = c = 9;
// 相当于 var a = 9; b = 9; c = 9; b 和 c 直接赋值 没有var 声明 当 全局变量看
// 集体声明 var a = 9, b = 9, c = 9;
console.log(a);
console.log(b);
console.log(c);
}
f1();
console.log(c);
console.log(b);
console.log(a);
三、ES6新特性
1. let和const关键字
区别:let表示声明变量,而const表示声明常量,两者都为块级作用域;const 声明的变量都会被认为是常量,意思就是它的值被设置完成后就不能再修改了。
需要注意:
-
let 关键词声明的变量不具备变量提升(hoisting)特性
-
let 和 const 声明只在最靠近的一个块中(花括号内)有效
-
当使用常量 const 声明时,请使用大写变量,如:CAPITAL_CASING
-
const 在声明时必须被赋值
2. 原型和原型链
一般情况下,我们的公共属性定义到构造函数里面, 公共的方法我们放到原型对象身上 如果我们修改了原来的原型对象,给原型对象赋值的是一个对象,则必须手动的利用constructor指回原来的构造函数
3. 模板字符串
基本的字符串格式化。将表达式嵌入字符串中进行拼接。用${}来界定;ES6反引号 `` 直接搞定;
$("body").html(`This demonstrates the output of HTML content to the page,
including student's ${name}, ${seatNumber}, ${sex} and so on.`);0
4. 扩展运算符
- 扩展运算符可以将数组拆分成以逗号分隔的参数序列
let ary = ["a", "b", "c"];
console.log(...ary)//a,b,c
- 扩展运算符应用于数组合并
let ary1 = [1, 2, 3];
let ary2 = [4, 5, 6];
let ary3 = [...ary1, ...ary2];
console.log(ary3)// [1, 2, 3, 4, 5, 6]
合并数组的第二种方法
let ary1 = [1, 2, 3];
let ary2 = [4, 5, 6];
ary1.push(...ary2);
console.log(ary1)// [1, 2, 3, 4, 5, 6]
- 利用扩展运算符将伪数组转换为真正的数组
var oDivs = document.getElementsByTagName('div');
console.log(oDivs)
var ary = [...oDivs];
ary.push('a');
console.log(ary);
5. 箭头函数
- 不需要 function 关键字来创建函数
- 省略 return 关键字
- 继承当前上下文的 this 关键字
6. 解构赋值
按照一定模式从数组或对象中提取值,然后对变量进行赋值(先提取,再赋值)
// 对象
const student = {
name: 'Sam',
age: 22,
sex: '男'
}
// 数组
// const student = ['Sam', 22, '男'];
// ES5;
const name = student.name;
const age = student.age;
const sex = student.sex;
console.log(name + ' --- ' + age + ' --- ' + sex);
// ES6
const { name, age, sex } = student;
console.log(name + ' --- ' + age + ' --- ' + sex);
7. Symbol
唯一标识,symbol作为对象的属性名时不会被for...in,for...of,Object.keys()识别;可以改用Reflect.ownkeys方法.
8. Promise
是一部编程的解决方案,状态不受外界影响
const p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve()
}, 1000)
}).then(res => {})
.catch(err => {})
9.Class类
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
其中:
- constructor方法是类的默认方法,通过new 命令生成对象时会调用该方法,如果声明类时没有定义constructor,会默认定义一个空的。
- 生成实例时必须用new ,不用会报错
- 不存在变里提升(选定义类,再new实例)
类的静态方法:
所有在类中定义的方法都会被实例继承,如果不想被继承,可以在定义时加上static。表示为静态方法。
class Foo {
static match() {}
}
Foo.match()
const f = new Foo()
f.match() // 报错
类的实例属性
类的方法默认被实例继承,那么属性呢?也是继承的,写法如下:
class Foo {
myProp = 111;
...
}
classr的继承 extends
class Point {}
class PointSon extends Point {
constructor(x, y, z) {
super(x, y)
this.z = z
}
}
其中:
- super等同于父类的constructor。
- 子类必须在constructor中调用super, 也就是说用extends去继承一个类,就必须调用这个类(父类)的constructor。是因为子类没有自己的this对象,而是继承父类的this,然后对其进行加工
- 如果了类没有写constructor,会默认生成一个,并包含了super(...args)
5. new关键字
5.1 new关键字执行过程
1. new 构造函数可以在内存中创建了一个空的对象
2. this 就会指向刚才创建的空对象
3. 执行构造函数里面的代码 给这个空对象添加属性和方法
4. 返回这个对象