本文为知识整理,可能工作中用到这些东西不多,可是总有人想让你会
前言:小时候很渴望长大,羡慕大人们的财富自由;长大后又很羡慕小时候,怀念小时候的无忧无虑,守候着那份天真。哦,还有,不是长大就能财富自由。。。
一:js 继承
①:对象冒充实现继承:(可实现多继承)
——原理:让父类构造函数成为子类的方法,然后调用子类的方法,通过 this 关键字给所有属性和方法赋值
function Parent(name)
{
this.name=name;
this.sayName=function () {
console.log(this.name);
}
}
function Child (cname) {
this.parent=Parent;
this.parent(cname);
delete this.parent; // 删除无用的 parent 函数 == f Parent(name)
}
var mychild=new Child(“ 名字 ”);
mychild.sayName();
②:原型链继承 (不能实现多继承)
prototype
function Parent (name, age) {
this.name = name;
this.age = age;
this.ParFunc = function () {
console.log(this.height)
}
};
Parent.prototype.sayName = function () {
console.log(this)
};
function Child (cname, cage, height) {
this.height = height
}
Child.prototype = new Parent(); // Child 原型指向 Parent 的一个实例
Child.prototype.constructor = Child; // 把 Child 指向自己,不与 Parent 共享
var child = new Child(‘ 测试名字 ’, ‘ 测试年龄 ’, ‘ 测试身高 ’)
console.log(child) // 发现打印出来的属性都继承了,就是没值
child.ParFunc():当访问 ParFunc 属性时,会先在 child 的实例属性中寻找,找不到就去 child 的原型对象上去找。一层一层的寻找构成了原型链因为无法向父类构造函数传参; 可以 new Parent(‘ 名字 ’, ‘ 年龄 ’)这时传参注:如果想用原型给 Child 拓展方法:
Child.prototype.childHeight = function () {
console.log(this.height)
}
一定要写到 Child.prototype = new Parent()的下面,要么就被覆盖掉了。
③:call、apply 继承(不能继承原型链,prototype 上的)
function Parent (name, age) {
this.name = name;
this.age = age;
this.ParFunc = function () {
console.log(this.name)
}
}
function Child (cname, cage, height) {
Parent.call(this,cname,cage); // 继承的参数为当前函数的形参
// apply: Parent.call(this,arguments);
this.height = height;
}
var child = new Child(‘ 测试名字 ’, ‘ 测试年龄 ’, ‘ 测试身高 ’)
console.log(child) // ParFunc: ƒ () age: “ 测试年龄 ”
name: “ 测试名字 ”
④:组合继承:call+ 原型链继承
function Parent (name, age) {
this.name = name;
this.age = age;
this.ParFunc = function () {
console.log(this.height)
}
}
Parent.prototype.sayName = function () {
console.log(this)
}
function Child (cname, cage, height) {
Parent.call(this, cname, cage); // 解决传参问题
this.height = height;
};
Child.prototype = new Parent()
Child.prototype.constructor = Child; // 把 Child 指向自己,否则一直指向 Parent
var child = new Child(‘ 测试名字 ’, ‘ 测试年龄 ’, ‘ 测试身高 ’)
比较常用的继承方式,缺点是 两次调用 Parent 构造函数
⑤:寄生组合继承:
cal+prototype
function Parent (name, age) {// 父函数
this.name = name;
this.age = age;
this.ParFunc = function () {
console.log(this.height)
}
}
Parent.prototype.sayName = function () {
console.log(this)
}
function Child (cname, cage, height) {// 子函数
Parent.call(this, cname, cage)
this.height = height;
};
var createObj = function () { // 中间函数继承 Parent
function Trans() {};
Trans.prototype = Parent.prototype;
return new Trans();
};
Child.prototype = createObj()
Child.prototype.constructor = Child; // 改回指针
var child = new Child(‘ 名字 ’, ‘ 年龄 ’, ‘ 身高 ’)
console.log(child)
二:如何获取自定义属性,特例 data-* 如何获取
官方定义:
data-* 是 **html5** 新属性
主要用于存储页面的自定义数据
不应该包含大写字母(会默认转为小写)
注释:用户代理会完全忽略前缀为 “data-” 的自定义属性。
脑海里第一印象就是 getAttribute(),setAttribute()俩属性了,一个获取,一个设置而平时又很少用到,但是平时用的框架什么的多数都用 data-* 这个自定义属性,那其实获取 data- 这类自定义属性的时候,还有个更方便的方法 dataset
<div data-a=”aa” id=”div1″ data-b=”bb”></div>
eg:var div1 = document.getElementById(‘div1’)
console.log(div1.dataset) // DOMStringMap {a: “ 测试 ”, b: “222”}a: “ 测试 ”b: “222”
用 data-* 作为自定义属性:可以一句话就获取到所有属性,获取方式也简便
三:事件的几个阶段:捕获、目标(event.target,即触发过程)、冒泡
先盗个图
——冒泡(IE 事件流):从最深的节点向外传播,div -> window, 就好比往水里丢一个石子,一层层波浪抵达河边缘
——捕获(网景):从最外层向目标传递,window -> div, 就好比你去一个大企业集团找人,需要提供公司 > 大部门 > 小部门 > 小组 > 目标
——目标:即触发过程 event.target
——target、currentTarget 的区别:target 这个属性指向的是目标过程中的 DOM 对象,也就是 触发事件监听的对象, currentTarget 这个指向的是当前的对象,具体内容跟 this 一样,当 this 指向的是目标的时候(事件的目标阶段),target 与 currentTarget 相同
——现在几乎所有的主流浏览器都支持冒泡;IE 早起版本会跳过 html 直接跳到 document,且不支持捕获。
——平时多数用到冒泡多一些,事件代理 (委托) 也是通过事件冒泡的原理,让 DOM 节点可追溯,也可以利用冒泡的原理做埋点,避免新增 DOM 节点,改代码上线
——事件句柄 addEventListener(‘ 事件名 ’, ‘function’, false),默认冒泡
四:判断数据类型,返回数据的具体类型
emm… 那不就直接 return typeof n 不就完了,哦不对,再识别一下数组,因为数组的 typeof 也是对象,Array.isArray(n)… /^12/ 这返回啥?wc, 也是 object 吧,那咋区分,对,正则有 test 方法,再判断一下
if (n.test) {
return ‘RegExp’
}
null 好像也返回 obj 吧,时间 Date 嘞,都返回 obj 也没毛病,万物皆对象啊。
据说 instanceof 也可以:左侧是否是右侧的实例,也就是说每个类型我们都得判断,于是
[] instanceof Array // true
[] instanceof Object // true
不光麻烦,返回的也不精确啊
据说 constructor 也可以:js 引擎会为函数添加 prototype, 并让其指向 ’ 该函数 ’ 的引用
/^12/.constructor // f RexExp(){[native code]}new Date().constructor // ƒ Date() {[native code] }null.constructor // 报错:Cannot read.. undefined.constructor // 报错:Cannot read.. 发现确实能校验一些 typeof 不能校验的,但是 null 和 undefined 没有 ’ 指针 ’ 啊, 而且写继承的时候,指针是可以被改的,稍不注意就凉凉了 …
把这些都整合到一起基本也都够用了,可是并不优雅
有请 toString 登场 …. 华丽分割线
toString() 是 Object 的原型方法,调用该方法,默认返回当前对象的 [[Class]]。这是一个内部属性,其格式为 [object Xxx],其中 Xxx 就是对象的类型。完美~~
Object.prototype.toString.call(null) ; // [object Null]
Object.prototype.toString.call(new Function()) ; // [object Function]
Object.prototype.toString.call(new Date()) ; // [object Date]
但是我觉得除了 obj 比较特殊之外,其他类型,typeof 都可以判断,不需要再多调用一次 toString 方法,所以最终封装 =>
function typeDetection (n) {
if (typeof n === ‘object’) {
return Object.prototype.toString.call(n)
} else {
return typeof n
}
}
直接调用 typeDetection(”) // string
五:千分位的实现
Q: 字符:1234567890.12 转换为:1,234,567,890.12
R: 个人用了 while 循环的方式
function strFilter (str) {
let newStr = String(str).split(‘.’) // 切分原始字符, 兼容传入 Number 类型
let transStr = newStr[0],resStr = []
while (transStr/1000 > 1) {// 判断是否大于 1000
let whildStr = String((transStr/1000).toFixed(3)).split(‘.’) // 这里一定要保留三位小数, 否则正数部分末位的 0 就会丢失, 又转为 String,因为 Number 没有 split 方法
transStr = whildStr[0] // 每次都取小数点以前的(正数部分)
resStr.unshift(whildStr[1]) // 向前插入小数点后的数字()
}
// 除以 1000 剩下的数 + 每次除以 1000 后的数 + 原来小数
let res2 = newStr[1]?(‘.’ + newStr[1]):”
let resComma = resStr.length?(‘,’ + resStr.join(‘,’)): ”
return transStr + resComma + res2
}
虽然实现代码很多,但个人觉得还算易懂
网上看到用正则的,确实简短:
function strFilter2 (n) {
let s = String(n)
let re = /\d{1,3}(?=(\d{3})+$)/g
let n1 = s.replace(/^(\d+)((\.\d+)?)$/, function (s, s1, s2) {return s1.replace(re, ‘$&,’) + s2 })
return n1
}
其实正则在好多场景都体现出优势,只是不能被轻易想到
Q: 以下 this 指向
(function () {
“use strict”;
console.log(this) // undefined
})()
(function () {
“use strict”;
console.log(this) // window
})()
R: 严格模式下,除构造函数、对象内置函数外,this 均指向 undefined
Q:script 标签的 async、defer 啥区别?R: 别说那没用的,上图
啥也不加:script 读取和解析脚本阶段都会阻断页面执行,加 async : script 解析脚本阶段会阻断页面执行加 defer : scriptj 脚本将在页面完成解析时执行
Q:[1,2,3].map(parseInt)的结果?R: 之前用到 parseInt,只知道是向下取整,翻了下 w3c 的 parseInt 定义: Crazy
再看 map,
parseInt 就是回调函数,map 会传给 parseInt 三个参数,parseInt 只识别前两个,那也就是得到了
function parseInt1 (item, index) {
return parseInt(item,index)
}
得到 parseInt(1,0) parseInt(2,1) parseInt(3,2)
parseInt(1,0),parseInt 定义 radix 不传或者 0, 按 10 进制,也就得到了 1
parseInt(2,1),parseInt 又定义第二个参数 radix 位于 2-36(除了 0),否则返回 NaN, 所以得到 NaN
parseInt(3,2), 这个据说 (网上) 是 3 不是 2 的合法进制数 (只有 0 和 1),但是个人试了试
parseInt(10,2) => 3,parseInt(20, 2) => 6,parseInt(30, 2) => NaN
, 发现只要是字符首字符小于 radix 都是可以的,但是一旦首字符 >= radix,就会返回 NaN
参考文章:判断 js 数据类型的四种方法