1.变量类型和计算
值类型和引用类型
值类型有string、number、boolean、undefined、null
引用类型:对象、数组、函数
// 值类型 a把自己的值复制一份给b,a修改了自己存储的100,而b的不改变,还是100
var a = 100;
var b = a;
a = 200;
//100
console.log(b);
// 引用类型 a把自己的值(这个值为一个对象的地址)复制给b一份,它们的指向相同,所以b改变的时候,a也改变了
var a = {age:20};
var b = a;
b.age = 21
//21
console.log(a.age);
typeof运算符
typeof可以分辨出基本数据类型,但是不能区分引用类型,除了function函数以外
typeof undefined //undefined
typeof "abc" //string
typeof 123 //number
typeof true //boolean
typeof {} //object
typeof [] //object
typeof null //object
typeof console.log //function
变量计算-隐式类型转换
转换规则
不同类型的变量比较要先转类型,叫做类型转换,类型转换也叫隐式转换。隐式转换通常发生在运算符加减乘除,等于,还有小于,大于等
比较时候的转换原则:
-
一个是number一个是string时,会尝试将string转换为number
-
尝试将boolean转换为number,0或1
-
尝试将Object转换成number或string,取决于另外一个对比量的类型
运算过程的转换代码
-
字符串与数字相加,变成字符串
-
字符串与数字相减,变成数字
当JavaScript遇到预期为布尔值的地方(比如if语句的条件部分),就会将非布尔值的参数自动转换为布尔值。系统内部会自动调用Boolean函数。
当JavaScript遇到预期为字符串的地方,就会将非字符串的数据自动转为字符串。系统内部会自动调用String函数。
字符串的自动转换,主要发生在加法运算时。当一个值为字符串,另一个值为非字符串,则后者转为字符串。
当JavaScript遇到预期为数值的地方,就会将参数值自动转换为数值。系统内部会自动调用Number函数。
除了加法运算符有可能把运算子转为字符串,其他运算符都会把运算子自动转成数值。
一元运算符也会把运算子转成数值。
字符串拼接
//110,number类型的
var a = 100 + 10
//"10010" string类型的
var b = 100 + '10'
//80 number类型的
var c = "100"-20;
==运算符
==运算符会隐式类型转换
//true ”100“转换为number 100
100 == "100";
//true ""转化为0
0 == ""
//true
null ==undefined;
if语句
var a = true
//a转为Boolean true
if(a){
}
var b = 100
//b转为Boolean true
if(b){
}
var c = ""
//c转化为 boolean false
if(c){
}
逻辑运算
JavaScript的逻辑运算符和其他语言(比如:java、c#、c等)有着很大的不同。
其他那些强类型的语言的逻辑运算符参数运算的都是true或false,结果也一定是true或false,很容器理解和记住。
而JavaScript的"逻辑运算符"参与运算的可以是任意类型,结果也可能是任意类型,规则及其复杂。
// JavaScript的逻辑运算符和其他语言(比如:java、c#、c等)有着很大的不同。
//其他那些强类型的语言的逻辑运算符参数运算的都是true或false,结果也一定是true或false,很容器理解和记住。
//而JavaScript的"逻辑运算符"参与运算的可以是任意类型,结果也可能是任意类型,规则及其复杂。
// 逻辑与 &&
//js中逻辑与和其他语言不太一样,如果第一个操作数是true(或者能够转为true),计算结果就是第二个操作数,如果第一个操作数是false,结果就是false(短路计算),对于一些特殊数值不遵循以上规则
//0
console.log(10&0)
console.log(undefined && false);//第一个操作数是undefined,结果undefined
console.log(NaN && false);//第一个操作数是NaN,结果NaN
console.log(null && false);//第一个操作数是null,结果null
console.log('' && false);//第一个操作数是空串,结果空串
// 逻辑或 ||
//如果第一个操作数不是false,结果就是第一个操作数,否则结果是第二个操作数。如果第一个操作数能够转为true,结果就是第一个操作数
//”abc“
console.log(""||"abc")
//true
console.log(!window.abc)
// 取反 ! 首先把数据转化为布尔值,然后取反,结果为true或false
var a = 100
//true
console.log(!!a)
答案
JS中使用typeof能得到哪种类型
何时使用===和==
if(obj.a == null){
//等价于obj.a === null ||obj.a == undefined jQuery
// 其他全部用===
}
JS中有哪些内置函数?
Number、String、Boolean、Array、Object、RegExp、Function、Date、Error
JS中有哪些内置对象?
Math和JSON
JS按存储方式区分变量类型
1.值类型
var a = 10;
var b = a;
a = 11;
//10
console.log(b);
2.引用类型
var obj1 = {x:100}
var obj2 = obj1
obj1.x = 200
//200
console.log(obj2.x);
如何理解JSON?
// JSON只不过是一个JS对象而已
JSON.stringify({a:10,b:20})
JSON.parse("{a:10,b:20}")
2.原型和原型链
构造函数
function Foo(name,age){
this.name = name;
this.age = age;
this.class = "class-1";
//默认会有这行
//return this;
}
var f = new Foo("zhangsan",20);
构造函数-扩展
原型规则和示例
// 1.所有的引用类型(数组、对象、函数),都具有对象特性,即可自由扩展属性(除了null)
var obj = {};
obj.a = 100;
var arr = [];
arr.a = 100;
function(){}
fn.a = 100;
// 2.所有的引用类型(数组、对象、函数),都具有__proto__属性(隐式原型),属性值是一个普通的对象
console.log(obj.__proto__);
console.log(arr.__proto__);
console.log(fn.__proto__);
// 3.所有的函数,都有一个prototype属性(显示原型),其值也为一个普通对象
console.log(fn.prototype);
// 4. 所有的引用类型(数组、对象、函数),__proto__属性指向它的构造函数的prototype属性值
console.log(obj.__proto__ === Object.prototype)
// 5.当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么会去它的__proto__(即它的构造函数的prototype)属性中寻找
//构造函数
function Foo(name){
this.name = name
}
Foo.prototype.alertName = function(){
alert(this.name);
}
//创建实例
var foo = new Foo("zhansan);
f.printName = function(){
console.log(this.name);
}
f.printName();
f.alertName();
// 6.循环对象自身的属性
var f
for(item in f){
//高级浏览器已经在for in中屏蔽了来自原型的属性
//但是这里建议大家还是加上这个判断,保证程序的健壮性
if(f.hasOwnProperty(item)){
console.log(item);
}
}
原型链
instanceof
答案
如何准确判断一个变量是数组类型
var arr = [];
//true
arr instanceof Array;
//true
Array.isArray(arr);
//object,是判断不出来的
typeof arr;
//true
Object.prototype.toString.call(arr) === "object Array";
var arr = [];
console.log(arr instanceof Array);
console.log(Array.isArray(arr));
console.log(Object.prototype.toString.call(arr) === "object Array");
写一个原型链继承的例子
function Animal(){
this.eat = function(){
console.log("animal eat.");
}
}
function Dog(){
//构造函数继承
Animal.call(this);
this.bark = function(){
console.log("dog bark.);
}
}
//借助原型链继承
Dog.prototype = Animal.prototype;
Dog.prototype.constructor = Dog;
var dog = new Dog();
描述new一个对象的过程
function Mynew(Func){
//创建一个对象,对象继承自Func.prototype
var newObj = Object.create(Func.prototype);
//执行Func()
var result = Func(newObj);
if(typeof result === "object"){
return result;
}else{
return newObj;
}
}
zepto(或其他框架)源码中如何使用原型
3.作用域和闭包
1. 变量提升
// 函数声明
//正常运行
fn();
//函数声明
function fn(){
//
}
// 变量声明提前 var fn1 = undefined;
//undefined is not a function
fn1();
//函数表达式
var fn1 = function(){
}
2.执行上下文
3.this
this要在执行时才能确认,定义时无法确认;
4.作用域
JS没有块级作用域
只有函数和全局作用域
作用域是在定义时确定的
5.作用域链
自由变量
6.闭包
函数作为返回值(闭包使用场景)
//1.函数作为返回值
function F1(){
var a = 100
//返回一个函数,即函数作为返回值
return function (){
console.log(a);
}
}
// f1得到一个函数
var f1 = F1();
var a = 200;
f1();//100
函数作为参数传递(闭包使用场景)
function F1(){
var a = 100;
return function (){
//自由变量,父作用域中寻找
console.log(a);
}
}
var f1 = F1();//返回一个函数
function F2(fn){
var a = 200
fn();
}
F2(f1);
//100
//注意:作用域是在定义的时候确定的!!!!!!
创建10ge<a>标签,点击的时候弹出来对应的序号
var i
for(i = 0;i<10;i++){
(function(){
var a = document.createElement("a");
a.innerHTML = i +"<br>";
a.addEventListener("click",funcyion(e){
e.preventDefault();
//自由变量,要去父作用域获取值
console.log(i);
})
document.body.appendChild(a);
})(i);
}
⭐闭包实际应用中主要用于封装变量,收敛权限
function isFirstLoad(){
var _list = [];
return function(id){
if(_list.indexOf(id)>=0){
return false;
}else{
_list.push(id);
return true;
}
}
}
//使用
var firstLoad = isFirstLoad();
firstLoad(10);// true
firstLoad(10);// false
firstLoad(20);// true
firstLoad(30);// true
firstLoad(40);// true
//在函数的外面是不可能修改掉_list的值的
4.异步和单线程
什么是异步(和同步对比)
输出结果为:100 300 200
如果是同步,那么会阻塞程序进行,而异步不会阻塞
如果是同步,那么会先输出100,然后等1000ms输出200,等待输出后,才输出300
何时需要异步?
在可能发生等待的情况
等待过程中不能像alert一样阻塞程序运行
因此,所有等待的情况都需要异步
前端使用异步的场景
定时器:setTimeout和setInterval
网络请求ajax和动态img、script加载
console.log("start");
var img = document.createElement("img");
img.onload = function(){
console.log("loaded.")
}
img.src = "https://www.wangbase.com/blogimg/asset/201903/bg2019301803.jpg"
console.log("end");
//输出:start end loaded.
console.log("start")
$.get("./data1.json",function(data){
console.log(data);
})
console.log("end.");
//输出 start end. 数据data的内容
事件绑定
console.log("start")
document.getElementById("btn1").addEventListener("click",function(data){
console.log("clicked.");
})
console.log("end.");
//输出start end. clicked.
ES6中的Promise
异步和单线程
5.日期和match
日期毫秒数从1970年开始到现在的毫秒数
6.数组和对象的API
数组API
对象API
var obj = {
x:100,
y:200,
z:300
}
var key
for(key in obj){
//注意这里的obj对象的hasOwnProperty()
console.log(obj.hasOwnProperty(key));
console.log(key);
console.log(obj);
if(obj.hasOwnProperty(key)){
console.log(key,obj[key]);
}
}
获取随机数,要求长度一致的字符串格式
var random = Math.random();
random+="0000000000"//后面加10个0
//
random.substring(0,10);
random.slice(0,10);
random.substr(0,10);
console.log("123456789012345678".substring(0,10));//"1234567890"
console.log("123456789012345678".slice(0,10));//"1234567890"
console.log("123456789012345678".substr(0,10));//"1234567890"
写一个能遍历对象和数组的forEach函数
function MyEach(obj,fn){
if(obj instanceof Array){
obj.forEach(function(item,index){
console.log(item);
fn(item);
})
}else{
for(var key in obj){
//遍历对象可以使用Object.keys(),for...in...,
console.log(key,obj[key]);
fn(key,obj[key]);
}
}
}